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

●问题描述:

  给出一个矩阵,其中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;
	}	//当四周都不通的时候将其置回0
	if (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("@");
			else
				printf(" ");
		}
		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("%%");
			else
				printf(" ");
		}
		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;
	else
		return 0;
}

//判栈满
int IsFull(STACK *maze)
{
	if (maze->top == MAXSIZE - 1)
		return 1;
	else
		return 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);
	else
		return 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("@");
			else
				printf(" ");
		}
		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("%%");
			else
				printf(" ");
		}
		printf("|\n");
	}
	for(i = 0; i <= COUNT_I; i++)
		printf("-");
	printf("\n");

	return 0;
}


●心得:

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

  • 14
    点赞
  • 36
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
好的,这是一个比较经典的算法题目,我来为您讲解一下思路。 首先,我们需要用一个二维数组来表示迷宫,1代表墙,0代表道路。假设起点为(0,0),终点为(m-1,n-1),其中m和n表示迷宫的行数和列数。 接下来,我们可以使用栈来实现非递归的深度优先搜索算法。具体步骤如下: 1. 将起点入栈,并将其标记为已经访问过。 2. 当栈不为空时,取出栈顶元素,判断其是否为终点。如果是,则说明已经找到了一条路径,输出路径并结束程序。如果不是,则继续执行下一步。 3. 遍历当前节点的邻居节点(上下左右四个方向),如果邻居节点未被访问过且为0,则将其入栈,并将其标记为已经访问过。 4. 重复步骤2和3,直到找到一条路径或者栈为空。 下面是用C语言实现的代码: ```c #include <stdio.h> #include <stdlib.h> #define MAXSIZE 100 typedef struct { int x, y; } Position; typedef struct { Position data[MAXSIZE]; int top; } Stack; void InitStack(Stack *S) { S->top = -1; } void Push(Stack *S, Position p) { S->data[++S->top] = p; } void Pop(Stack *S) { S->top--; } Position Top(Stack S) { return S.data[S.top]; } int IsEmpty(Stack S) { return S.top == -1; } int Maze[MAXSIZE][MAXSIZE]; int Visited[MAXSIZE][MAXSIZE]; int m, n; void PrintPath(Stack S) { while (!IsEmpty(S)) { Position p = Top(S); printf("(%d,%d)\n", p.x, p.y); Pop(&S); } } int main() { scanf("%d%d", &m, &n); for (int i = 0; i < m; i++) { for (int j = 0; j < n; j++) { scanf("%d", &Maze[i][j]); Visited[i][j] = 0; } } Stack S; InitStack(&S); Position start = {0, 0}; Push(&S, start); Visited[start.x][start.y] = 1; while (!IsEmpty(S)) { Position cur = Top(S); if (cur.x == m-1 && cur.y == n-1) { PrintPath(S); return 0; } // 上 if (cur.x > 0 && Maze[cur.x-1][cur.y] == 0 && !Visited[cur.x-1][cur.y]) { Position next = {cur.x-1, cur.y}; Push(&S, next); Visited[next.x][next.y] = 1; continue; } // 下 if (cur.x < m-1 && Maze[cur.x+1][cur.y] == 0 && !Visited[cur.x+1][cur.y]) { Position next = {cur.x+1, cur.y}; Push(&S, next); Visited[next.x][next.y] = 1; continue; } // 左 if (cur.y > 0 && Maze[cur.x][cur.y-1] == 0 && !Visited[cur.x][cur.y-1]) { Position next = {cur.x, cur.y-1}; Push(&S, next); Visited[next.x][next.y] = 1; continue; } // 右 if (cur.y < n-1 && Maze[cur.x][cur.y+1] == 0 && !Visited[cur.x][cur.y+1]) { Position next = {cur.x, cur.y+1}; Push(&S, next); Visited[next.x][next.y] = 1; continue; } // 所有邻居节点都已经访问过,出栈 Pop(&S); } printf("No path found\n"); return 0; } ``` 注意,这里用到了一个结构体Stack,实现了栈的基本操作。另外,为了方便起见,我们将起点和终点的坐标分别定义为{0,0}和{m-1,n-1},并且将Visited数组用来记录每个节点是否已经被访问过。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值