迷宫问题算法
迷宫问题一直是计算机工做者感兴趣的问题,由于它能够展示栈的巧妙应用,数组
这里将利用栈开发一个走迷宫程序,虽然在发现正确路径前,程序要尝试许多布局
错误路径,可是,一旦发现,就可以从新走出迷宫,而不会再去尝试任何错误路径。测试
迷宫问题求解spa
计算机中能够用如图所示的方块图表示迷宫。图中空白方块为通道,蓝色方块为墙3d
迷宫的储存可使用二维数组,其中“0”表明墙值,“1”表明通路。因为迷宫被表示为指针
二维数组,因此,在任什么时候刻,迷宫中的位置均可以用行,列坐标来描述。调试
在迷宫中的某一个位置进行移动的可能方向如图所示code
值得注意的是,并非每个位置都有四个邻居。若是[row][col]在边界上那么邻居的blog
个数就少于4个,甚至只有2个。为了不边界条件的检查,在迷宫周围加上一圈边界。这样,
一个m*n的迷宫就须要一个(m+2)*(n+2)的数组。入口位置在[1][1],而出口位置在[m][n]。
另外一个简化问题的策略是,用数值direc 预先定义出“可能的移动方向”数字0-3表示4个
可能的移动方向,对每一个方向都指出其垂直和水平的偏移量。
求迷宫中一条径的算法的基本思想是:
若当前位置“可通“,则归入”当前路径”,并继续朝“下一个位置探索”,即切换“下一位置”为
“当前位置”,如此反复直到出口;
若当前位置“不可通”则应顺着“来向”退回到“前一通道块”,而后朝着除“来向”以外的其余方向继续探索;
若该通道块的四周4个方块均“不可通”,则应从“当前路径”上删除该通道块。假设以栈
S记录“当前路径”,则栈顶中存放的是“当前路径上最后一个通道块”。由此,
“归入路径”的操做即为“当前位置压入”,“从当前路径上删除前一块通道块”的操做即为“弹出”。
须要说明的是,所谓的当前位置可通,指的是不曾走到过的通道块,即要求该方块位置S记录“当前路径”,
则栈顶中存放的是“当前路径上最后一个通道块”。由此,“归入路径”的操做即为“当前位置压入”,
“从当前路径上删除前一块通道块”的操做即为“弹出”。不只是通道块,并且不在当前路径上(不然路径不是简单路径),
也不是曾经归入过路径的通道块(不然只能在死胡同内转圈)。
sqstack.h
1 #pragma once 2 #include <stdio.h> 3 #include <stdlib.h> 4 #define STACK_INIT_SIZE 100//储存空间初始分配量 5 #define STACKINCREMENT 10//存储空间分配增量 6 #define OK 1 7 #define ERROR 0 8 typedef struct { 9 int x; //行值 10 int y; //列值 11 }PosType; //迷宫坐标位置类型 12 typedef struct 13 { 14 int ord; //序号 15 int di; //方向 16 PosType seat; //位置 17 18 } StackType; //栈元素类型 19 20 typedef struct { 21 StackType *base; //在构造以前和销毁以后,base的值为NULL 22 StackType *top; //栈顶指针 23 int stacksize; //当前已分配的存储空间,以元素为单位 24 25 }SqStack; //顺序栈 26 27 //栈的初始化 28 int InitStack(SqStack *p) { 29 30 31 p->base = (StackType*)malloc(STACK_INIT_SIZE * sizeof(StackType)); 32 if (p->base == NULL) return ERROR; //内存分配失败 33 p->top = p->base; //栈顶与栈底相同表示一个空栈 34 p->stacksize = STACK_INIT_SIZE; 35 return OK; 36 37 } 38 //判断栈是否为空 39 int EmptyStack(SqStack *p) { 40 //若为空栈 则返回OK,不然返回ERROR 41 if (p->top == p->base) return OK; 42 else return ERROR; 43 } 44 //顺序栈的压入 45 int Push(SqStack *p, StackType e) { 46 //插入元素e为新的栈顶元素 47 if ((p->top - p->base) >= p->stacksize) //栈满,追加储存空间 48 { 49 p->base = (StackType*)realloc(p->base, (p->stacksize + STACKINCREMENT) * sizeof(StackType)); 50 if (p->base == NULL) return ERROR;// 储存空间分配失败 51 p->top = p->base + p->stacksize; 52 p->stacksize += STACKINCREMENT; 53 } 54 *(p->top) = e; 55 (p->top)++; 56 return OK; 57 } 58 // 顺序栈的弹出 59 int Pop(SqStack *p, StackType *e) { 60 //若栈不空,则删除p的栈顶元素,用e返回其值 61 if (p->top == p->base) return ERROR; 62 --(p->top); 63 *e = *(p->top); 64 return OK; 65 66 67 } 68 //将顺序栈置空 栈仍是存在的,栈中的元素也存在, 69 //若是有栈中元素的地址任然能调用 70 int ClearStack(SqStack *p) { 71 p->top = p->base; 72 return OK; 73 } 74 //顺序栈的销毁 75 int DestroyStack(SqStack *p) { 76 //释放栈底空间并置空 77 free(p->base); 78 p->base = NULL; 79 p->top = NULL; 80 p->stacksize = 0; 81 82 return OK; 83 }
源代码:
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include "sqstack.h" //引入顺序栈储存结构及其基本操做 4 #define MAXLENGTH 25 //设迷宫最大行列为25 5 #define ERROR 0 6 #define OK 1 7 #define TRUE 1 8 #define FALSE 0 9 typedef int Status; 10 typedef int MazeType[MAXLENGTH][MAXLENGTH]; //迷宫数组[行][列] 11 PosType begin, end; //迷宫入口坐标,出口坐标 12 PosType direc[4] = { {0,1},{1,0},{0,-1},{-1,0} }; //{行增量,列增量},移动方向依次为东南西北 13 MazeType maze; //迷宫数组 14 int x, y;//迷宫的行,列 15 int curstep = 1; //当前足迹,初值在入口处为1 16 struct SElemType 17 { 18 int ord; //通道块在路径上的“序号” 19 PosType seat; //通道块在迷宫中的“坐标位置” 20 int di; //今后通道块走向下一通道块的“方向”(0-3表示东-北) 21 }; 22 void Print() { 23 //输出迷宫结构 24 int i, j; 25 for (i = 0; i < x; i++) 26 { 27 for (j = 0; j < y; j++) 28 printf("%3d", maze[i][j]); 29 printf("\n"); 30 } 31 } 32 void Init() 33 { 34 //设定迷宫布局(墙值为0,通道值为1) 35 //全局变量maze 未初始化 因此均为值均为0 36 int i, j, x1, y1; 37 printf("请输入迷宫的行数,列数(包括外墙):"); 38 scanf("%d%d", &x, &y); 39 for (i = 1; i < x - 1; i++) 40 for (j = 1; j < y - 1; j++) 41 maze[i][j] = 1; //定义通道初值为1 42 printf("请输入迷宫内墙单元数:"); 43 scanf("%d", &j); 44 printf("请依次输入迷宫内墙每一个单元的行数,列数:\n"); 45 //FILE *fp; 注释掉的这五行以前是方便我调试的 46 //fp = fopen("maze.txt", "r"); 47 //int *px1 = &x1, *py1 = &y1; 48 for(i=1;i<=j;i++) 49 { 50 //fscanf(fp, "%d%d", px1, py1); 51 //maze[x1][y1] = 0; 52 scanf("%d%d", &x1, &y1); 53 maze[x1][y1] = 0; //定义墙值为0 54 } 55 printf("迷宫结构以下:\n"); 56 Print(); 57 printf("请输入入口的行数,列数:"); 58 scanf("%d%d", &begin.x, &begin.y); 59 printf("请输入出口的行数,列数:"); 60 scanf("%d%d", &end.x, &end.y); 61 } 62 void MarkPrint(PosType seat) 63 {//标记迷宫块不可经过 64 maze[seat.x][seat.y] = -1; 65 66 67 } 68 Status Pass(PosType b) 69 { 70 //当迷宫m的b点的值为1,return OK; 71 //不然return ERROR; 72 if (maze[b.x][b.y] == 1) 73 return OK; 74 else 75 return ERROR; 76 } 77 void FootPrint(PosType b) 78 { 79 //使迷宫m的b点的值变为足迹(curstep),表示通过 80 maze[b.x][b.y] = curstep; 81 82 } 83 PosType NextPos(PosType b,int di) 84 { 85 //根据当前位置b及其移动方向di,修改b为下一位置 86 b.x += direc[di].x; 87 b.y += direc[di].y; 88 return b; 89 } 90 Status MazePath(PosType start, PosType end) 91 { 92 //若迷宫m中存在从入口start到出口end的通道 93 //则求得一条存放在栈中,并返回TRUE;不然返回FAlSE 94 SqStack s,*S; //顺序栈 95 S = &s; 96 PosType curpos; 97 StackType e,*pe; 98 pe = &e; 99 InitStack(S); 100 curpos = start; 101 do 102 { 103 if (Pass(curpos)) //当前能够经过,则是不曾走到的通道块 104 { 105 FootPrint(curpos); //留下足迹 106 e.ord = curstep; //栈元素序号为当前序号 107 e.seat = curpos; //栈元素位置为当前位置 108 e.di = 0; //从当前位置出发,下一位置为东 109 Push(S, e); //入栈当前位置及其状态 110 curstep++; //足迹加1 111 if (curpos.x == end.x&&curpos.y == end.y) //到达出口 112 return TRUE; 113 curpos = NextPos(curpos, e.di); //由当前位置及移动方向肯定下一个当前位置 114 } 115 else //当前位置不能经过 116 if (!EmptyStack(S)) //栈不为空 117 { 118 Pop(S, pe); // 退栈到前一位置 119 curstep--; //足迹减1 120 while (e.di == 3 && !EmptyStack(S)) //前一位置处于最后一个方向(北) 121 { 122 MarkPrint(e.seat); //留下不能经过的标记(-1) 123 Pop(S, pe); //退一步 124 curstep--; //足迹再减1 125 126 } 127 if (e.di < 3) //没有到最后一个方向(北) 128 { 129 e.di++; //换下一个方向探索 130 Push(S, e); //入栈该位置的下一个方向 131 curstep++;// 足迹加1 132 curpos = NextPos(e.seat, e.di); 133 134 } 135 136 } 137 138 } 139 while (!EmptyStack(S)); 140 return FALSE; 141 142 143 } 144 int main() 145 { 146 Init(); //初始化迷宫 147 if (MazePath(begin, end)) //有通路 148 { 149 printf("今后迷宫入口到出口的一条路径以下:\n"); 150 Print(); 151 } 152 else 153 printf("此迷宫没有从入口到出口的路径\n"); 154 return 0; 155 156 157 } 158
测试以下:
须要注意的是获得的路径并非最短路径。要求最短路径应该把全部
路径求出,再比较栈的大小 。最小长度的栈则保存着最短的路径。