数据结构与算法——栈 迷宫算法 回溯法
找迷宫通路需要使用回溯法,找迷宫通路是对回溯法的一个很好的应用,实现回溯的过程用到数据结构—栈!
回溯法:对一个包括有很多个结点,每个结点有若干个搜索分支的问题,把原问题分解为若干个子问题求解的 算法;当搜索到某个结点发现无法再继续搜索下去时,就让搜索过程回溯(回退)到该节点的前一个结点,继续 搜索该节点外的其他尚未搜索的分支;如果发现该结点无法再搜索下去,就让搜索过程回溯到这个结点的前一 结点继续这样的搜索过程;这样的搜索过程一直进行到搜索到问题的解或者搜索完了全部可搜索分支没有解存 在为止。
代码实现
maze.h
1 #pragma once 2 #include<stdio.h> 3 #include<stdlib.h> 4 5 #define MAXSIZE 100 //预先分配空间,这个数值根据实际需要预估确定 6 7 //存储坐标的结构体 _x; _y; 8 typedef struct _Position 9 { 10 int _x; 11 int _y; 12 }Position; 13 14 typedef Position ElemType; //栈中存储的类型 Position 存储坐标的结构体 _x; _y; 15 16 //栈 包含栈顶与栈底指针 17 typedef struct _SqStack 18 { 19 ElemType* base = NULL; //栈底指针 20 ElemType* top = NULL; //栈顶指针 21 }SqStack; 22 23 //构造一个空栈 24 bool InitStack(SqStack& S) 25 { 26 S.base = new ElemType[MAXSIZE]; //为顺序栈分配一个最大容量为 MAXSIZE 的空间 27 if (!S.base) //空间分配失败 28 return false; 29 S.top = S.base; //top 初始为 base,空栈 30 31 return true; 32 } 33 34 // 插入元素 e 为新的栈顶元素 35 bool PushStack(SqStack &S, ElemType e) 36 { 37 if (S.top - S.base == MAXSIZE) //栈满 38 return false; 39 40 *(S.top++) = e; //元素 e 压入栈顶,然后栈顶指针加 1,等价于*S.top=e; S.top++; 41 42 return true; 43 } 44 45 //删除 S 的栈顶元素,暂存在变量 e 中 46 bool PopStack(SqStack& S, ElemType& e) 47 { 48 if (S.base == S.top) //栈空 49 { 50 return false; 51 } 52 e = *(--S.top); //栈顶指针减 1,将栈顶元素赋给 e 53 54 return true; 55 } 56 57 //返回 S 的栈顶元素,栈顶指针不变 58 ElemType* GetTop(SqStack& S) 59 { 60 if (S.top != S.base) //栈非空 61 { 62 return S.top - 1; //返回栈顶元素的值,栈顶指针不变 63 } 64 else 65 { 66 return NULL; 67 } 68 } 69 70 //返回栈中元素个数 71 int GetSize(SqStack& S) 72 { 73 return (S.top - S.base); 74 } 75 76 //判断栈是否为空 77 bool IsEmpty(SqStack& S) 78 { 79 if (S.top == S.base) 80 { 81 return true; 82 } 83 else 84 { 85 return false; 86 } 87 } 88 89 //销毁栈 90 void DestoryStack(SqStack& S) 91 { 92 if (S.base) 93 { 94 free(S.base); 95 S.base = NULL; 96 S.top = NULL; 97 } 98 }
maze.cpp
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <string.h> 4 #include "maze.h" 5 #include <assert.h> 6 7 #define ROW 6 //坐标系宽度 8 #define COL 6 //坐标系高度 9 10 //储存迷宫地图 11 typedef struct _Maze 12 { 13 int map[ROW][COL]; 14 }Maze; 15 16 //迷宫的初始化 17 void InitMaze(Maze* m, int map[ROW][COL]) 18 { 19 if (m == NULL || map == NULL) 20 return; 21 22 for (int i = 0; i < ROW; ++i) 23 { 24 for (int j = 0; j < COL; ++j) 25 { 26 m->map[i][j] = map[i][j]; 27 } 28 } 29 } 30 31 void PrintMaze(Maze* m) //打印迷宫 32 { 33 for (int i = 0; i < ROW; ++i) 34 { 35 for (int j = 0; j < COL; ++j) 36 { 37 printf("%d ", m->map[i][j]); 38 } 39 printf("\n"); 40 } 41 printf("\n"); 42 } 43 44 //判断是否是有效的入口 45 int IsValidEnter(Maze* m, Position cur) 46 { 47 assert(m); 48 49 if ((cur._x == 0 || cur._x == ROW - 1) || (cur._y == 0 || cur._y == COL - 1) && (m->map[cur._x][cur._y] == 1)) 50 return 1; 51 else 52 return 0; 53 } 54 55 //判断当前节点的下一个节点能否走通 56 int IsNextPass(Maze* m, Position cur, Position next) 57 { 58 assert(m); 59 //判断 next 节点是否为 cur 的下一节点 60 if ( 61 ((next._x == cur._x) && ((next._y == cur._y + 1) || (next._y == cur._y - 1))) //在同一行上并且相邻 62 || 63 ((next._y == cur._y) && ((next._x == cur._x + 1) || (next._x == cur._x - 1))) //或在同一列上并且相邻 64 ) 65 { 66 //判断下一个节点是否在迷宫里面 67 if (((next._x >= 0 && next._x < ROW) || (next._y >= 0 && next._y < COL)) && (m->map[next._x][next._y] == 1)) 68 { 69 return 1; 70 } 71 } 72 return 0; 73 } 74 75 //判断当前节点是不是有效的迷宫出口 76 int IsValidExit(Maze* m, Position cur, Position enter) 77 { 78 assert(m); 79 //这里首先得保证该节点不是入口点,其次只要它处在迷宫的边界即可 80 if ((cur._x != enter._x || cur._y != enter._y) && ((cur._x == 0 || cur._x == ROW - 1) || (cur._y == 0 || cur._y == COL - 1))) 81 return 1; 82 else 83 return 0; 84 } 85 86 //找迷宫通路 Maze* m 地图, Position enter入口, SqStack* s 栈空间 87 int PassMaze(Maze* m, Position enter, SqStack* s) 88 { 89 assert(m && IsValidEnter(m, enter) == 1); //对给的迷宫的入口进行合法性判断 90 91 Position cur = enter; //当前判断坐标 92 Position next; 93 PushStack(*s, cur); //首先将迷宫的入口压入栈中 94 m->map[cur._x][cur._y] = 2; //将入口值改为 2 95 96 //PrintMaze(m); 97 while (!IsEmpty(*s)) 98 { 99 cur = *GetTop(*s); 100 //printf("cur: %d %d\n",cur._x, cur._y); 101 if (IsValidExit(m, cur, enter) == 1) //判断当前位置是否出口 102 return 1; 103 104 //尝试向左一步:看当前节点的左一个节点能不能走通 105 next = cur; 106 next._y = cur._y - 1; 107 if (IsNextPass(m, cur, next) == 1) 108 { 109 PushStack(*s, next); 110 m->map[next._x][next._y] = m->map[cur._x][cur._y] + 1; 111 //PrintMaze(m); 112 continue; 113 } 114 115 //尝试向上一步:看当前节点的上一个节点能不能走通 116 next = cur; 117 next._x = cur._x - 1; 118 if (IsNextPass(m, cur, next) == 1) //next 节点能够走通时,将其压入栈中 119 { 120 PushStack(*s, next); 121 m->map[next._x][next._y] = m->map[cur._x][cur._y] + 1; //将 next 节点的值等于 cur 节点的值加 1 122 //PrintMaze(m); 123 continue; 124 } 125 126 //右:看当前节点的向右的一个节点能不能走通 127 next = cur; 128 next._y = cur._y + 1; 129 if (IsNextPass(m, cur, next) == 1) 130 { 131 PushStack(*s, next); 132 m->map[next._x][next._y] = m->map[cur._x][cur._y] + 1; 133 //PrintMaze(m); 134 continue; 135 } 136 137 //下:看当前节点的下一个节点能不能走通 138 next = cur; 139 next._x = cur._x + 1; 140 if (IsNextPass(m, cur, next) == 1) 141 { 142 PushStack(*s, next); 143 m->map[next._x][next._y] = m->map[cur._x][cur._y] + 1; 144 //PrintMaze(m); 145 continue; 146 } 147 148 //走到这里说明当前节点的四个方向都走不通,进行回溯,看前一个节点未被遍历的方向是否还能走通 149 Position tmp; 150 PopStack(*s, tmp); 151 } 152 return 0; 153 } 154 int main() 155 { 156 //用二维数组描绘迷宫:1 代表通路,0 代表墙 157 int map[ROW][COL] = 158 { 159 0,0,1,0,0,0, 160 0,0,1,1,1,0, 161 0,0,1,0,0,0, 162 0,1,1,1,1,0, 163 0,0,1,0,1,0, 164 0,0,0,0,1,0 165 }; 166 167 Maze m; //迷宫地图 168 ElemType enter; //迷宫入口 169 enter._x = 0; 170 enter._y = 2; 171 172 InitMaze(&m, map); 173 PrintMaze(&m); 174 175 //system("pause"); 176 //exit(0); 177 178 SqStack s; //定义栈,保存已走过的坐标轨迹,便于回溯 179 InitStack(s); //栈的初始 180 int ret = PassMaze(&m, enter, &s); //使用栈和回溯法解开迷宫 181 if (ret) 182 { 183 printf("恭喜你!终于找到了出口~\n"); 184 } 185 else 186 { 187 printf("不是我笨!实在没有出口~\n"); 188 } 189 190 PrintMaze(&m); 191 system("pause"); 192 193 return 0; 194 }