●问题描述:

  给出一个矩阵,其中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;
}


●非递归思路:

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;
}


●心得:

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