递归与非递归实现走迷宫算法

来源:互联网 发布:淘宝女装服装店铺简介 编辑:程序博客网 时间:2024/06/06 19:03

●问题描述:

  给出一个矩阵,其中0表示通路,1表示墙壁,这样就形成了一个迷宫,要求编写算法求出其中一条路径。

●递归思路:

  编写一个走迷宫函数,传入二位数组的下标,先假设该点位于最终路径上(将0置为2)再探测周围四个点是否可以走通(是否为0),如果可以走通则将该点四周能走通的点作为函数参数传入函数进入递归。若四周均不能走通(都不为0时)则将该点置回0表示该点不是最终路径上的点。

  在此思路中递归进入时表示了枚举路径,当发现此条路径走到某处再不能走通时就将路径该点置回0并且递归退出(回溯)寻找下一条可走通路径。

●代码:

#include <stdio.h>#include <stdlib.h>#define END_I 8#define END_J 7#define START_I 0#define START_J 0//走迷宫int VistMaze(int maze[][8], int i, int j){int end = 0;//假设能够走通maze[i][j] = 2;//如果到达重点则将end置为0表示迷宫已经走结束if (i == END_I && j == END_J){end = 1;}//如果迷宫没有走结束则将搜索所在位置的右、下、左、上四个方向是否能够走通if (end != 1 && j + 1 <= END_J && maze[i][j + 1] == 0){//右if (VistMaze(maze, i, j + 1) == 1)return 1;}if (end != 1 && i + 1 <= END_I && maze[i + 1][j] == 0){//下if (VistMaze(maze, i + 1, j) == 1)return 1;}if (end != 1 && j - 1 >= START_J && maze[i][j - 1] == 0){//左if (VistMaze(maze, i, j - 1) == 1)return 1;}if (end != 1 && i - 1 >= START_I && maze[i - 1][j] == 0){//上if (VistMaze(maze, i - 1, j) == 1)return 1;}//当四周都不通的时候将其置回0if (end != 1){maze[i][j] = 0;}return end;}int main(void){    //迷宫    int i, j;    int maze[9][8] = {        {0,0,1,0,0,0,1,0},        {0,0,1,0,0,0,1,0},        {0,0,0,0,1,1,0,1},        {0,1,1,1,0,0,1,0},        {0,0,0,1,0,0,0,0},        {0,1,0,0,0,1,0,1},        {0,1,1,1,1,0,0,1},        {1,1,0,0,1,1,0,1},        {1,1,0,0,0,0,0,0}        };//打印出迷宫printf("原迷宫:\n");for(i = 0; i <= 9; i++)printf("-");printf("\n");for (i = 0; i < 9; i++){printf("|");for (j = 0; j < 8; j++){if (maze[i][j] == 1)printf("@");elseprintf(" ");}printf("|\n");}for(i = 0; i <= 9; i++)printf("-");printf("\n");if (VistMaze(maze, 0, 0) == 0){printf("没有路径可走\n");exit(0);}//打印出迷宫和路径printf("迷宫和路径:\n");for(i = 0; i <= 9; i++)printf("-");printf("\n");for (i = 0; i < 9; i++){printf("|");for (j = 0; j < 8; j++){if (maze[i][j] == 1)printf("@");else if (maze[i][j] == 2)printf("%%");elseprintf(" ");}printf("|\n");}for(i = 0; i <= 9; i++)printf("-");printf("\n");return 0;}

●非递归思路:

  此题也可以使用栈来避开递归,首先需要开辟一个和迷宫具有同样几何形状的结构体二维数组来保存是从哪一个点(坐标)到达该点的(该初始化时将所有的坐标都置为-1),还需要一个可以保存坐标的栈。每次将能够走通的点(值为0)都入栈然后在循环的开始处将栈顶元素弹出保存进temp变量(保存坐标的变量)中,访问temp变量四周的元素是否可以被访问,如果可以被访问(值为0)则将对应路径数组中的元素(即对应坐标)值改为temp变量的值并将该能被访问的变量入栈,再将迷宫中的0(能访问)更改为2(已被访问过)防止重复访问。最后从路径数组中的出口处开始倒序输出所走路径即可。

  在此种思想中栈保存了所有可以走通的点,当一个点的四周都不能够走通时弹出的该点坐标程序并没有进行任何实质性的处理,所以这就相当于一个“回溯”的过程。而访问四周的点的过程就是一个枚举的过程。


●代码:


#include <stdio.h>#include <stdlib.h>#define COUNT_I 9#define COUNT_J 8#define START_I 0#define START_J 0#define END_I 8#define END_J 7#define MAXSIZE 1000//坐标位置结构体typedef struct local{int x;int y;}LOCAL;//栈结构typedef struct stack{LOCAL data[MAXSIZE];int top;}STACK;//初始化栈STACK *InitStack(void){STACK *maze;maze = (STACK *)malloc(sizeof(STACK));maze->top = -1;return maze;}//判栈空int EmptyStack(STACK *maze){if (maze->top == -1)return 1;elsereturn 0;}//判栈满int IsFull(STACK *maze){if (maze->top == MAXSIZE - 1)return 1;elsereturn 0;}//入栈int PushStack(STACK *maze, LOCAL *x){if (maze->top <= MAXSIZE - 1){maze->data[++maze->top] = *x;return 1;}else{printf("栈已满\n");return 0;}}//出栈int PopStack(STACK *maze, LOCAL *x){if (maze->top > -1){*x = maze->data[maze->top];maze->top--;return 1;}else{printf("栈已空\n");return 0;}}//走迷宫函数int VistMaze(int maze[][COUNT_J], LOCAL path[][COUNT_J]){int i, j;//初始化栈STACK *stack;LOCAL temp;stack = InitStack();temp.x = 0; temp.y = 0;if (maze[START_I][START_J] == 0)PushStack(stack, &temp);elsereturn 0;while(!EmptyStack(stack)){PopStack(stack, &temp);i = temp.x;j = temp.y;maze[i][j] = 2;if (i == END_I && j == END_J)break;//下if (i + 1 <= END_I && maze[i + 1][j] == 0){maze[i + 1][j] = 2;path[i + 1][j].x = i;path[i + 1][j].y = j;temp.x = i + 1;temp.y = j;PushStack(stack, &temp);}//右if (j + 1 <= END_J && maze[i][j + 1] == 0){maze[i][j + 1] = 2;path[i][j + 1].x = i;path[i][j + 1].y = j;temp.x = i;temp.y = j + 1;PushStack(stack, &temp);}//左if (j - 1 >= 0 && maze[i][j - 1] == 0){maze[i][j - 1] = 2;path[i][j - 1].x = i;path[i][j - 1].y = j;temp.x = i;temp.y = j - 1;PushStack(stack, &temp);}//上if (i - 1 >= 0 && maze[i - 1][j] == 0){maze[i - 1][j] = 2;path[i - 1][j].x = i;path[i - 1][j].y = j;temp.x = i - 1;temp.y = j;PushStack(stack, &temp);}}//如果到达终点而退出的循环则将路径标识出来if (i == END_I && j == END_J){maze[i][j] = 3;while(path[temp.x][temp.y].x != -1){temp = path[temp.x][temp.y];maze[temp.x][temp.y] = 3;}return 1;}else{return 0;}}int main(void){//迷宫int i, j;int maze[COUNT_I][COUNT_J] = {{0,0,1,0,0,0,1,0},{0,0,1,0,0,0,1,0},{0,0,0,0,1,1,0,1},{0,1,1,1,0,0,1,0},{0,0,0,1,0,0,0,0},{0,1,0,0,0,1,0,1},{0,1,1,1,1,0,0,1},{1,1,0,0,1,1,0,1},{1,1,0,0,0,0,0,0}};//定义路径数组,将到(x,y)点的路径保存进数组LOCAL path[COUNT_I][COUNT_J];for(i = 0; i < COUNT_I; i++){for(j = 0; j < COUNT_J; j++){path[i][j].x = -1;path[i][j].y = -1;}}//打印出迷宫printf("原迷宫:\n");for(i = 0; i <= COUNT_I; i++)printf("-");printf("\n");for (i = 0; i < COUNT_I; i++){printf("|");for (j = 0; j < COUNT_J; j++){if (maze[i][j] == 1)printf("@");elseprintf(" ");}printf("|\n");}for(i = 0; i <= COUNT_I; i++)printf("-");printf("\n");if (VistMaze(maze, path) == 0){printf("没有路径可走\n");exit(0);}//打印出迷宫和路径printf("迷宫和路径:\n");for(i = 0; i <= COUNT_I; i++)printf("-");printf("\n");for (i = 0; i < COUNT_I; i++){printf("|");for (j = 0; j < COUNT_J; j++){if (maze[i][j] == 1)printf("@");else if (maze[i][j] == 3)printf("%%");elseprintf(" ");}printf("|\n");}for(i = 0; i <= COUNT_I; i++)printf("-");printf("\n");return 0;}


●心得:

  这两种算法都离不开枚举与回溯这两大思想,在以后的学习中要多加理解这两种思想。在初次接触时理解不了递归的过程,后来在之上慢慢画模拟计算机的执行过程终于让递归这个过程了然于胸。可见在编程中验算是多么的重要。

1 0