C++数据结构学习笔记(一)栈的应用——迷宫问题

题目描述:

        有一个 m×n 格的迷宫(表示有 m 行、n 列),其中有可走的也有不可走的,如果用 0 表示可以走,1表示不可以走,起始点(1,1)、结束点(8,8)(起始点和结束点都是用两个数据来描述的,分别表示这个点的行号和列号)。现在要你编程找出所有可行的道路,要求所走的路中没有重复的点,走时只能是上下左右四个方向。如果一条路都不可行,则输出相应信息

算法思路:

        迷宫问题需要考虑的一个点是当此路不通时需要进行回退,且回退次数不确定,这种回退的思想与栈的“后进先出”很契合,所以选择用栈来实现该算法,将每走一步的位置存储在栈中,当进行回退时只要出栈即可。具体实现方式如下。

一、迷宫构造        

        用二维数组存储迷宫,0代表可行道路,1代表墙壁。首先设置地图初始化函数和显示函数

//迷宫设置
void mazeset(int (&maze)[LENGTH][WIDTH])
{
	//边框设值
	for (int i = 0; i < WIDTH; i++)
	{
		if (i == 0 || i == LENGTH - 1)
		{
			for (int j = 0; j < LENGTH; j++)
			{
				maze[i][j] = 1;
			}
		}
		else
		{
			for (int j = 1; j < LENGTH - 1; j++)
			{
				maze[i][j] = 0;
			}
			maze[i][0] = 1;
			maze[i][WIDTH - 1] = 1;
		}
	}
	//地图设值    此处可以改为手动输入设置墙壁障碍
	maze[1][3] = 1;
	maze[2][3] = 1;
	maze[1][7] = 1;
	maze[2][7] = 1;
	maze[3][5] = 1;
	maze[3][6] = 1;
	maze[4][2] = 1;
	maze[4][3] = 1;
	maze[4][4] = 1;
	maze[5][4] = 1;
	maze[6][2] = 1;
	maze[6][6] = 1;
	maze[7][2] = 1;
	maze[7][3] = 1;
	maze[7][4] = 1;
	maze[7][6] = 1;
	maze[7][7] = 1;
	maze[8][1] = 1;
	//cout << maze[0][0] << endl;
}

//迷宫显示
void mazeshow(int(&maze)[LENGTH][WIDTH])
{
	for (int i = 0; i < WIDTH; i++)
	{
		for (int j = 0; j < LENGTH; j++)
		{
			cout << maze[i][j]<<"\t ";
		}
		cout <<endl<< endl;
	}
}

        显示效果:

 二、方向试探

//方向试探
typedef struct
{
	//			[-1][0]上
	//[0][-1]左			[0][1]右
	//			[ 1][0]下
	int incX, incY;
}Direction;

        构造一个结构体Direction用来表示方向,(-1,0)(1,0)(0,-1)(0,1)分别表示上下左右,由于目标位置处于初始位置的右下方,所以按照“右下左上”的顺序对其进行初始化。

    //Direction direct[4] = {(0,1),(1,0),(0,-1),(-1,0)};//这种赋值方法会出bug,没搞明白
	Direction direct[4];
	direct[0].incX = 0;
	direct[0].incY = 1;
	direct[1].incX = 1;
	direct[1].incY = 0;
	direct[2].incX = 0;
	direct[2].incY = -1;
	direct[3].incX = -1;
	direct[3].incY = 0;

即在右边位置可行时尝试下方、下方不可行时尝试左方、左方不可行时尝试上方。当四个方向都不可行时进行退步。

三、移动位置存储

        采用栈来对位置进行存储,首先构造一个结构体Box来表示位置。di用做direct的索引,0,1,2,3分别代表右下左上。

//栈中元素
typedef struct
{
	//栈中元素为三元组,x,y坐标和方向
	int x,y;
	int di;
}Box;

        其次是栈的构造,可以直接使用<stack.h>中的栈,但本文属于栈的学习和应用,所以手动构建了一个模板栈。

//栈
template <class DataType>
class Stack 
{
private:
	DataType* data;
	int size;
	int top;
public:
	Stack();
	Stack(int s);
	~Stack();
	void push(DataType ch);//入栈
	DataType pop();//出栈
	DataType getTop();//获取栈顶元素
	bool isEmpty();//判断栈是否为空
	bool isFull();//是否栈满
	void setNULL();//将栈置为空
	class Full {};//异常内部类,直接抛出即可
	class Empty {};

};

        模板栈的优势在于可以适应任意数据类型,不受栈中元素类型限制,在使用时确定其数据类型即可。

        所有准备工作做好以后即可开始实现路径寻找算法(算法步骤注释很详细,如果还是看不懂可参考B站懒猫老师视频讲解,本文即视频算法思路的实现):

bool findPath(int maze[LENGTH][WIDTH],Direction direct[],Stack<Box> &s)
{
	//寻找路径的顺序:右下左上
	Box temp;
	int x, y, di;//迷宫格子当前处理单元的纵横坐标和方向
	int line, col;//迷宫下一单元的横纵坐标
	maze[1][1] = -1;//起始点,-1表示来过
	temp.x = 1;
	temp.y = 1;
	temp.di = -1;
	//temp = { 1,1,-1 };
	s.push(temp);//初始点压入栈中,此处di赋值为-1是为了进入第一个while循环后进行方向试探
	while (!s.isEmpty())
	{
		temp = s.pop();//栈顶元素出栈后对该元素进行操作
						//首个元素会进行出栈操作,其后的元素仅在第二个while循环条件不满足,即四个方向都不可达时才会进行出栈操作(出栈操作相当于向后退一步,若退后一步可以找到一个新方向则向新方向前进,若找不到则再次回退),若某个方向可达时只会进行入栈,而不会出栈。
		x = temp.x;
		y = temp.y;
		di = temp.di+1;
		while (di < 4)//四个方向进行尝试,当四个方向都尝试完其都不可达时,
					//再次进入第一个while循环,从栈中取出新的栈顶元素
		{
			//下一步的横纵坐标
			line = x + direct[di].incX;
			col = y + direct[di].incY;
			//下一步若可到达
			if (maze[line][col] == 0)
			{
				temp = { x,y,di };
				//记录当前所走的位置,即压入栈中
				s.push(temp);
				x = line;
				y = col;
				maze[line][col] = -1;
				//注释掉部分用于画出路径箭头,效果不太好
				//if (di == 0)
				//{
				//	maze[line][col] = -1;
				//}
				//else if (di == 1)
				//{
				//	maze[line][col] = -2;
				//}
				//else if (di == 2)
				//{
				//	maze[line][col] = -3;
				//}
				//else if (di == 3)
				//{
				//	maze[line][col] = -4;
				//}
				if (x == M && y == N)
				{
					cout << endl;
					for (int i = 0; i < WIDTH; i++)
					{
						for (int j = 0; j < LENGTH; j++)
						{
							cout << maze[i][j] << "\t";
							//也是画箭头
							//if (maze[i][j] == 0 || maze[i][j] == 1)
							//{
							//	cout << maze[i][j]<<"\t";
							//}
							//else if (maze[i][j] == -1)
							//{
							//	cout << "->" << "\t";
							//}
							//else if (maze[i][j] == -2)
							//{
							//	cout << "||" << "\t";
							//}
							//else if (maze[i][j] == -3)
							//{
							//	cout << "<-" << "\t";
							//}
							//else if (maze[i][j] == -4)
							//{
							//	cout << "^|" << "\t";
							//}
						}
						cout << endl << endl;
					}
					return true;
				}
				else
					di = 0;
			}
			//下一步若不可到达则按顺序进行下一个方向的尝试
			else 
				di++;
		}
		
	}
	cout << endl;
	//mazeshow(maze);//mazeshow函数会参数报错,没搞明白
	//这块是为了在找不到路是看一下是怎么走的,
	for (int i = 0; i < WIDTH; i++)
	{
		for (int j = 0; j < LENGTH; j++)
		{
			cout << maze[i][j] << "\t ";
		}
		cout << endl << endl;
	}
	return false;
}

最终实现效果(最后一行1即代表找到可到达路径):

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值