【数据结构】基于递归策略和栈解决迷宫问题

 

在处理迷宫问题的时候,我想起了王道机试指南上的一端文字: 

深度优先搜索,它类似于人在迷宫中寻找出口:
每遇到一个路口,先往一个既定的方向走到底,直到发现出口或遇到死胡同。
发现死胡同后,就回到上一个路口,并选择另外一个方向继续寻找出口。

事实上,在处理迷宫问题时,我们也是使用这种思路。下面来介绍:


1. 问题描述

我们这样描述迷宫:设置迷宫为二维数组,数组的值为

-1:代表墙
0:代表未走过的路径
1:代表走不通的路径
2:代表路径

我们使用一个二维数组myMap[10][10]来存储本文中将要使用的迷宫:

int myMap[10][10] =
{
	-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
	-1, 0, 0,-1, 0, 0, 0,-1, 0,-1,
	-1, 0, 0,-1, 0, 0, 0,-1, 0,-1,
	-1, 0, 0, 0, 0,-1,-1, 0, 0,-1,
	-1, 0,-1,-1,-1, 0, 0, 0, 0,-1,
	-1, 0, 0, 0,-1, 0, 0, 0, 0,-1,
	-1, 0,-1, 0, 0, 0,-1, 0, 0,-1,
	-1, 0,-1,-1,-1, 0,-1,-1, 0,-1,
	-1,-1, 0, 0, 0, 0, 0, 0, 0,-1,
	-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
};

并做出以下假设:迷宫入口为myMap[1][1],迷宫出口为myMap[8][8]。

 


2. 解决方案

我们尝试使用递归策略来解决问题。

首先我们使用是一个二维数组direction[4][2],来设置搜索方向:

int direction[4][2] =
{
	{1,0},{-1,0},{0,1},{0,-1}
};

并按下列算法解决问题:

1. 如果当前位置为出口,结束。

2. 否则:

  • 假设当前位置为路径,向某方向前进一步
  • 依次遍历四个方向,若四个方向均走不通,返回上一步所在的位置(回溯)。

 


3. 代码

//递归解决迷宫问题

#include<iostream>
#include<cstdio>

using namespace std;

int myMap[10][10] =
{
	-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
	-1, 0, 0,-1, 0, 0, 0,-1, 0,-1,
	-1, 0, 0,-1, 0, 0, 0,-1, 0,-1,
	-1, 0, 0, 0, 0,-1,-1, 0, 0,-1,
	-1, 0,-1,-1,-1, 0, 0, 0, 0,-1,
	-1, 0, 0, 0,-1, 0, 0, 0, 0,-1,
	-1, 0,-1, 0, 0, 0,-1, 0, 0,-1,
	-1, 0,-1,-1,-1, 0,-1,-1, 0,-1,
	-1,-1, 0, 0, 0, 0, 0, 0, 0,-1,
	-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
};

struct Point {
	int row;
	int col;
	Point(int x, int y) :row(x), col(y) {}
};

int direction[4][2] =
{
	{1,0},{-1,0},{0,1},{0,-1}
};

int Goal(Point cur, Point end)
{
	if (cur.row == end.row && cur.col == end.col)
	{
		return 1;
	}
	else
	{
		myMap[cur.row][cur.col] = 2;
		for (int i = 0; i < 4; i++)
		{
			int nx = cur.row + direction[i][0];
			int ny = cur.col + direction[i][1];

			//如果遇到障碍物
			if (nx < 0 || nx>9 || ny < 0 || ny>9 || (myMap[nx][ny] != 0))
				continue;
			Point t(nx, ny);
			if (Goal(t, end))  return 1;
		}
		myMap[cur.row][cur.col] = 1;

	}
	return 0;
}


void Print()
{
	for (int i = 0; i < 10; i++)
	{
		for (int j = 0; j < 10; j++)
		{
			if (j == 9)
				printf("%2d", myMap[i][j]);
			else
				printf("%2d,", myMap[i][j]);
		}
		printf("\n");
	}
	return;
}

//打印迷宫
void PrintPath()
{
	for (int i = 0; i < 10; i++)
	{
		for (int j = 0; j < 10; j++)
		{
			if (myMap[i][j] == -1) cout << "■";
			else if (myMap[i][j] == 2) cout << " *";
			else cout << "□";
		}
		cout << endl;
	}
	cout << endl;
}


int main()
{
	Point start(1, 1);
	Point end(8, 8);

	printf("\nBefore : \n");
	Print();

	Goal(start, end);

	printf("\nAfter : \n");
	Print();

	printf("\nPath :\n");
	PrintPath();

	return 0;
}

 


4. 拓展:使用栈来解决迷宫问题

看起来,思路好像很简单。实际上笔者花费了大半天的时间才看明白。。虽然一部分原因拜vs的逐步调试所赐。。

相比之下,使用栈来解决迷宫问题的代码相当简洁:

//栈解决迷宫问题

#include<iostream>
#include<cstdio>
#include<stack>

using namespace std;

int myMap[10][10] =
{
	-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
	-1, 0, 0,-1, 0, 0, 0,-1, 0,-1,
	-1, 0, 0,-1, 0, 0, 0,-1, 0,-1,
	-1, 0, 0, 0, 0,-1,-1, 0, 0,-1,
	-1, 0,-1,-1,-1, 0, 0, 0, 0,-1,
	-1, 0, 0, 0,-1, 0, 0, 0, 0,-1,
	-1, 0,-1, 0, 0, 0,-1, 0, 0,-1,
	-1, 0,-1,-1,-1, 0,-1,-1, 0,-1,
	-1,-1, 0, 0, 0, 0, 0, 0, 0,-1,
	-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
};

struct Point {
	int row;
	int col;
	Point(int x,int y):row(x),col(y){}
};

void MasePath(Point start, Point end)
{
	stack <Point> PointStack;
	Point P = start;
	myMap[P.row][P.col] = 2;
	do {
		PointStack.push(P);
		if (myMap[P.row][P.col + 1] == 0)    myMap[P.row][++P.col] = 2;
		else if (myMap[P.row + 1][P.col] == 0)  	myMap[++P.row][P.col] = 2;
		else if (myMap[P.row][P.col - 1] == 0)     myMap[P.row][--P.col] = 2;
		else if (myMap[P.row - 1][P.col] == 0)	myMap[--P.row][P.col] = 2;
		else
		{
			P = PointStack.top();
			PointStack.pop();
			myMap[P.row][P.col] = 1;
			P = PointStack.top();
			PointStack.pop();
		}
	} while ((P.row != end.row) || (P.col != end.col));

	return;
}


void Print()
{
	for (int i = 0; i < 10; i++)
	{
		for (int j = 0; j < 10; j++)
		{
			if(j==9) 
				printf("%2d", myMap[i][j]);
			else
				printf("%2d,", myMap[i][j]);
		}
		printf("\n");
	}
	return;
}

//打印迷宫
void PrintPath()
{
	for (int i = 0; i < 10; i++)
	{
		for (int j = 0; j < 10; j++)
		{
			if (myMap[i][j] == -1) cout << "■";
			else if (myMap[i][j] == 2) cout << " *";
			else cout << "□";
		}
		cout << endl;
	}
	cout << endl;
}


int main()
{
	Point start(1, 1);
	Point end(8, 8);

	printf("\nBefore : \n");
	Print();

	MasePath(start, end);

	printf("\nAfter : \n");
	Print();

	printf("\nPath :\n");
	PrintPath();

	return 0;
}

其解决思路类似:

1. 当前位置入栈

2. 判断下一步是否可行,

若可行    ,则返回步骤1

若不可行,则换方向继续尝试

3. 若四个方向均不可行,则当前位置出栈。回到前一个位置,换方向搜索。

 


5. 总结

哎呀。。迷宫问题也算是一个经典问题了

不过我只能想出来第一种递归方法,使用栈进行处理的操作还是需要进一步理解。

如果说迷宫问题的变体的话,那就是如何输出迷宫的所有可能路径呢???

留给以后来探索...

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值