首页 > 代码库 > 使用栈和队列实现迷宫路径查找算法

使用栈和队列实现迷宫路径查找算法

0 综述

最近,接到老板的一个小任务,即实现个迷宫路径查找小程序,要求使用栈和队列去实现。不敢怠慢,我赶紧打开Visual Studio 2012,同时在稿纸上笔画着。题目如下:使用栈及队列分别设计一个算法进行迷宫路径查找(用下列二维数组表示迷宫,1表示不可通过,0表示可以通过,左上角2为起点,右下角3为终点)。


1. 栈实现

    迷宫路径查找的关键点在于对分支点的处理,使用栈来存储分支点坐标的实现方法称之为深度搜索算法。对于初始矩阵,从初始点2开始搜索四个方向上的路径并分别对路径进行合理标记,然后在路径查找时可根据标记来输出查找到的路径。所以在查找过程中正确的标记方案将决定路径查找的效率及可行性。对于栈来说,可以采用如下标记策略:

按照题目中的值,2为起始点,3为终点,则令flag=4即下一个合理的路径节点权值从4开始。

规则一:对于当前节点,只有唯一路径出口的,将出口节点权值标记为当前节点权值,如图中4(起始点权值为4),5,7的点。

规则二:将分支点压入栈,每次出栈的点在原有权值基础上加1,如4节点有三个出口被压入栈,5为第一个出栈的,6为第二个出栈的。

技术分享

按照如上规则标记权值,搜索路径时可根据从起点按照上下左右四个路径查找权值最大的点作为路径下一个节点,即可找查找到最终路径(2-4-6-7-3)。


2. 队列实现

    使用队列来存储分支点坐标的实现方法称之为广度搜索算法。和上述栈的查找方法不同之处在于,标记策略的不同。对于队列来说,可以采用如下标记策略:

按照题目中的值,2为起始点,3为终点,和上述栈一致,从4开始标记路径。

规则一:对于同一级别的广度搜索节点采用统一的权值。

规则二:记录同一级别点的个数,当进入下一级别队列时,权值加1

技术分享

按照如上规则标记权值,搜索路径时可采用逆向搜索方法即从终点前搜索最小值作为路径有效节点(3-9-8-7-6-5-4-2)。


3. 实现代码与结果演示

/* =========================================================
*                   迷宫路径搜索算法
*  =========================================================
*  20141203
*/

#include <iostream>
#include <fstream>
#include <ctime>
#include <stack>
#include <queue>

using std::cout;
using std::endl;
using std::stack;
using std::queue;

#define N 15

typedef struct{
	int x;
	int y;
}PointPos;

int map[15][15] = { 
			{ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
			{ 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
            { 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1 },
            { 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1 },
            { 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1 },
            { 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1 },
            { 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1 },
            { 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1 },
            { 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1, 1 },
            { 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1 },
            { 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1, 1 },
            { 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1 },
            { 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1 },
            { 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 1 },
            { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }
};

/* =========================================================
*						函数声明
*  =========================================================
*/
// 搜寻起始点和终点坐标函数
void FindMarkedPos(PointPos& start,PointPos& end);

// 恢复map初始数据函数
void InitMap();

// 基于Stack的深度优先搜索算法
void UseStack(PointPos start,PointPos end);

// 基于Queue的广度优先搜索算法
void UseQueue(PointPos start,PointPos end);

// Stack结果输出函数
void StackOutPut(PointPos start);

// Queue结果输出函数
void QueueOutPut(PointPos start);

/* =========================================================
*						主函数
*  =========================================================
*/
void main(void)
{
	// 其实结束标记点定义
	PointPos start,end;

	// 寻找标记点函数
	FindMarkedPos(start,end);

	// Stack深度优先搜索函数
	UseStack(start,end);
	
	// 输出 Stack深度优先搜索的结果
	cout<<"Stack - Search Result:"<<endl;
	StackOutPut(start);
	cout<<endl;

	// 恢复map数据
	InitMap();

	// Queue广度优先搜索函数
	UseQueue(start,end);

	// 输出 Queue广度优先搜索的结果
	cout<<"Queue - Search Result:"<<endl;
	QueueOutPut(end);

}

/* =========================================================
*               搜寻起始点和终点坐标函数
*  =========================================================
*/
void FindMarkedPos(PointPos& start,PointPos& end)
{
	// 找到起点和终点坐标
	int flag=0;
	stack<PointPos> pointstack;

	for(int i=0; i<15; i++)
	{
		for(int j=0; j<15; j++)
		{
			if(2 == map[i][j])
			{
				start.x = i;
				start.y = j;
				flag = flag + 1;
			}
			else if(map[i][j] == 3)
			{
				end.x = i;
				end.y = j;
				flag = flag + 1;
			}

			if(2 == flag) break;
		}
		if(2 == flag) break;
	}
}


/* =========================================================
*           基于Stack的深度优先搜索算法
*  =========================================================
*/
void UseStack(PointPos start,PointPos end)
{
	// 定义存储栈
	stack<PointPos> pointstack;
	// 标记路径权值,定义点坐标和临时变量
	int flag = 4;
	PointPos pointpos,pointtemp;
	
	// 将起点坐标入栈,并进入循环搜索
	pointstack.push(start);

	while(!pointstack.empty())
	{
		pointpos = pointstack.top();
		pointstack.pop();

		// count记录每个坐标点有路径的方向个数
		int i,j,count=0; 
		i = pointpos.x;
		j = pointpos.y;
		//cout<<i<<'\t'<<j<<endl;

		//如果为搜索过的路径,置为权值
		if( 0 == map[i][j]) map[i][j] = flag;

		// 坐标点上下是否可走
		if(i>0 && i<N-1)
		{
			if(0 == map[i-1][j])
			{
				pointtemp.x = i-1;
				pointtemp.y = j;
				pointstack.push(pointtemp);
				++count;
			}
			else if(3 == map[i-1][j])
				break;

			if(0 == map[i+1][j])
			{
				pointtemp.x = i+1;
				pointtemp.y = j;
				pointstack.push(pointtemp);
				++count;
			}
			else if(3 == map[i+1][j])
				break;
		}
		// 坐标点左右是否可走
		if(j>0 && j<N-1)
		{
			if(0 == map[i][j-1])
			{
				pointtemp.x = i;
				pointtemp.y = j-1;
				pointstack.push(pointtemp);
				++count;
			}
			else if(3 == map[i][j-1])
				break;

			if(0 == map[i][j+1])
			{
				pointtemp.x = i;
				pointtemp.y = j+1;
				pointstack.push(pointtemp);
				++count;
			}
			else if(3 == map[i][j+1])
				break;
		}

		// 关于有多条通路的处理
		if(1 != count)
		{
			++flag;
		}
	}
}

/* =========================================================
*				恢复map初始数据函数
*  =========================================================
*/
void InitMap()
{
	for(int i=0; i<N; i++)
		for(int j=0; j<N; j++)
		{
			// 标记为路径
			if(-1 == map[i][j])
				map[i][j] = 0;
			// 恢复原来的值
			else if(map[i][j] > 3)
				map[i][j] = 0;
		}
}

/* =========================================================
*           基于Queue的广度优先搜索算法
*  =========================================================
*/
void UseQueue(PointPos start,PointPos end)
{
	// 定义存储队列
	// 分支点的权值为等同,即队列同等级同权值
	// 采用逆向搜索路径的算法,这样分支点可实现唯一路径查找
	queue<PointPos> pointqueue;
	queue<int> countqueue;

	// 标记路径权值,定义点坐标和临时变量
	// Count为一次广度的同级别点个数
	int flag = 3,Count=1,count=0;
	PointPos pointpos,pointtemp;
	
	// 将起点坐标入队列,并进入循环搜索
	pointqueue.push(start);

	while(!pointqueue.empty())
	{
		pointpos = pointqueue.front();
		pointqueue.pop();
		if(Count<1 && !countqueue.empty())
		{
			Count = countqueue.front();
			countqueue.pop();
		}
		if(0 == Count) continue;

		--Count;
		
		// count记录每个坐标点有路径的方向个数
		int i,j; 
		i = pointpos.x;
		j = pointpos.y;
		//cout<<i<<'\t'<<j<<endl;

		//如果为搜索过的路径,置为权值
		if( 0 == map[i][j]) map[i][j] = flag;

		// 坐标点上下是否可走
		if(i>0 && i<N-1)
		{
			if(0 == map[i-1][j])
			{
				pointtemp.x = i-1;
				pointtemp.y = j;
				pointqueue.push(pointtemp);
				++count;
			}
			else if(3 == map[i-1][j])
				break;

			if(0 == map[i+1][j])
			{
				pointtemp.x = i+1;
				pointtemp.y = j;
				pointqueue.push(pointtemp);
				++count;
			}
			else if(3 == map[i+1][j])
				break;
		}
		// 坐标点左右是否可走
		if(j>0 && j<N-1)
		{
			if(0 == map[i][j-1])
			{
				pointtemp.x = i;
				pointtemp.y = j-1;
				pointqueue.push(pointtemp);
				++count;
			}
			else if(3 == map[i][j-1])
				break;

			if(0 == map[i][j+1])
			{
				pointtemp.x = i;
				pointtemp.y = j+1;
				pointqueue.push(pointtemp);
				++count;
			}
			else if(3 == map[i][j+1])
				break;
		}
		

		if(Count<1)
		{
			countqueue.push(count);
			// 同级别的广度点赋予的值相同
			++flag; 
			// 重新赋值
			count = 0;
		}

	}

}

/* =========================================================
*						 结果输出函数
*  =========================================================
*/
void StackOutPut(PointPos start)
{
	// 搜索到达的路径
	int i,j,ni,nj,max;
	i = start.x;
	j = start.y;
	max = map[i][j];

	while(3 != map[i][j])
	{
		// 上下判断
		if(i>0 && i<N-1)
		{
			if(map[i-1][j] > max)
			{
				ni = i-1;
				nj = j;
				max = map[i-1][j];
			}
			if(3 == map[i-1][j])
				break;

			if(map[i+1][j] > max)
			{
				ni = i+1;
				nj = j;
				max = map[i+1][j];
			}
			if(3 == map[i+1][j])
				break;
		}
		// 左右判断
		if(j>0 && j<N-1)
		{
			if(map[i][j-1] > max)
			{
				ni = i;
				nj = j-1;
				max = map[i][j-1];
			}
			if(3 == map[i][j-1])
				break;

			if(map[i][j+1] > max)
			{
				ni = i;
				nj = j+1;
				max = map[i][j+1];
			}
			if(3 == map[i][j+1])
				break;
		}
		
		//cout<<ni<<'\t'<<nj<<'\t'<<max<<endl;
		// 标记为路径 即 -1
		map[ni][nj] = -1;
		// 设置初始最大值
		max = 3;
		// 进入迭代
		i = ni;
		j = nj;
	}

	// 输出结果
	for(int i=0; i<N; i++)
	{
		for(int j=0; j<N; j++)
		{
			// 标记为路径
			if(-1 == map[i][j])
				cout<<'-'<<' ';
			// 回复原来的值
			else if(map[i][j] > 3)
				cout<<0<<' ';
			else
				cout<<map[i][j]<<' ';
		}
		cout<<endl;
	}
}

/* =========================================================
*						 结果输出函数
*  =========================================================
*/
void QueueOutPut(PointPos end)
{
	// 搜索到达的路径
	int i,j,ni,nj,w;
	i = end.x;
	j = end.y;
	ni = i;
	nj = j;
	w = map[i][j];
	// 获取权值
	if(i>0 && i<N-1)
	{
		if(map[i-1][j] > w)
		{
			ni = i-1;
			w = map[i-1][j];
		}
		if(map[i+1][j] > w)
		{
			ni = i+1;
			w = map[i+1][j];
		}
	}

	if(j>0 && j<N-1)
	{
		if(map[i][j-1] > w)
		{
			nj = j-1;
			w = map[i][j-1];
		}

		if(map[i][j+1] > w)
		{
			nj = j+1;
			w = map[i][j+1];
		}
	}

	i = ni;
	j = nj;

	while(2 != map[i][j])
	{
		// 上下判断
		if(i>0 && i<N-1)
		{
			if(map[i-1][j] == w-1)
			{
				map[i][j] = -1;
				i = i-1;
				j = j;
				--w;
				continue;
			}
			else if(map[i+1][j] == w-1)
			{
				map[i][j] = -1;
				i = i+1;
				j = j;
				--w;
				continue;
			}
			else if(map[i-1][j] == 2 || map[i+1][j] == 2)
				break;
		}
		// 左右判断
		if(j>0 && j<N-1)
		{
			if(map[i][j-1] == w-1)
			{
				map[i][j] = -1;
				i = i;
				j = j-1;
				--w;
				continue;
			}
			else if(map[i][j+1] == w-1)
			{
				map[i][j] = -1;
				i = i;
				j = j+1;
				--w;
				continue;
			}
			else if(map[i][j-1]==2 || map[i][j+1]==2)
				break;
		}
	
	}
	// 最后补充为-1 因为设置权值时 2-4 非连续
	map[i][j] = -1;

	// 输出结果
	for(int i=0; i<N; i++)
	{
		for(int j=0; j<N; j++)
		{
			// 标记为路径
			if(-1 == map[i][j])
				cout<<'-'<<' ';
			// 回复原来的值
			else if(map[i][j] > 3)
				cout<<0<<' ';
			else
				cout<<map[i][j]<<' ';
		}
		cout<<endl;
	}
}

技术分享







使用栈和队列实现迷宫路径查找算法