【数据结构Note3】- 栈 应用栈求解迷宫路径

栈求解迷宫路径

本文分为两部分,第一部分让大家整体了解栈求解迷宫路径的整体思路和算法实现,第二部分仔细分析迷宫求解过程。小白门看不懂第一部分没关系,可以直接跳到下一部分,从定义迷宫开始往后看。

1. 求解迷宫思想和难点

求解迷宫路径整体思路就是穷举所有路径。从点出发,按某一方向探索,沿途在经过的位置设下标记。若能走通即某处可以到达。若该点所有方向均没有通路,则原返回,直到有路可走。如此循环直到到达终点,或无路可走返回起点。求解过程会用一个栈path存储迷宫路径上的所有有效坐标。

求解开始时,path栈首先存储起点坐标,此时栈顶就是起点坐标。内部循环,不断根据栈顶坐标(也就是当前坐标)和迷宫图得到下一步行走方向。

  • 有路可走则将下一个坐标入栈作为当前坐标,进入下一个循环。
  • 如果无路可走则出栈返回前一个结点,换方向尝试

求解迷宫穷举路径的难点就在于,如何在原路返回前一个点查找路径时不会重复的到达某点,形成死循环?

  1. 方案一是另外设置一标志数组 flag[n],其所有元素都初始化为0,一旦到达了某点(ij)之后,将对应flag]置1,下次再试探该位置时,因为已经置1了,就不能再选它了;缺点时有些浪费空间了撒
  2. 方案二是当到达某点(i,j)后将对应maze[i][j]置-1,其他未到达过的点其值只能是1或0,可与未到达过的点区别开来。这里采用方案二实现算法。

整体了解完迷宫求解算法,下面便是重点分析了

2. 定义迷宫

image-20221016112303375

如上图,对于长M,宽N的迷宫,需要在迷宫周围围上墙。所以定义的迷宫,我们用二级指针maze来表示迷宫,对应的为 mazeSample[M + 2][N + 2],整个迷宫图内部0标识通路,1表示围墙。

//定义迷宫数组的长宽M,N
const int M = 8;
const int N = 8;

//定义并初始化迷宫二维数组
int mazeSample[M + 2][N + 2] =
{
    {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}
};

int** maze = (int**)malloc(sizeof(int*) * (M+2));
for (int i = 0; i < M+2; i++)
{
    maze[i] = (int*)malloc(sizeof(int) * (N+2));
}
for (int i = 0; i < M+2; i++)
{
    for (int j = 0; j < N+2; j++)
    {
        maze[i][j] = mazeSample[i][j];
    }
}
//输出迷宫
for (int i = 0; i < M+2; i++)
{
    for (int j = 0; j < N+2; j++)
    {
        cout << maze[i][j]<<" ";
    }
    cout << endl;
}

image-20221019150408850

3. 定义方向结构体和数组

定义direct数组,数组元素为Direction结构体。Direction结构体存储两个整型变量,incX和intY分别表示该方向xy方向的增量。

当前坐标cur为:(x,y)

cur下一步移动的方向为:direct[v]

下一步的坐标next为:(x+direct[v].incX,y+direct[v].incY)

image-20221016112839084

上图中数组从上往下分别对应下右上左四个方向

//定义方向结构体
struct Direction
{
    //incX和intY分别表示该方向xy方向的增量
	int incX, incY;
};
//共四个方向,定义存储四个结构体的数组
Direction direct[4] = { {0,1},{1,0},{0,-1},{-1,0} };

//定义点坐标
struct pointer{
    int x;
    int y;
};

4. 迷宫路径穷举

路径存储在pointer类型的storage栈中。

迷宫路径穷举函数findPath的实现思路是:将起点压入栈storage中,不断根据getDirection函数寻找下一个合适的坐标,并将坐标压入栈中,再将其迷宫对应数值设为-1,防止重复穷举路径。如果没有合适坐标,说明当前是死胡同,则出栈,在前一个坐标寻找新路径。具体如下:

//根据当前结点获得下一步方向,返回整数temp,对应direct[temp]即为方向。若返回值则说明无路可走为
int getDirection(int** maze, pointer cur)
{
	int index = 0;
	while (index < 4)
	{
		//判断迷宫中direct[index]对应的方向是否可以走,可以则返回index作为下一步方向标识
		if (cur.x + direct[index].incX<N+1
			&& cur.y + direct[index].incY<M+1
			&&maze[cur.x + direct[index].incX][cur.y + direct[index].incY] == 0)return index;
		index++;
	}
	return 4;
}

//求解迷宫的函数,如果迷宫有解,就返回true。如果没解就返回false
bool findPath(int** maze, stack<pointer>& s)
{
	pointer cur;
	cur.x = 1;
	cur.y = 1;//cur就是起点
	maze[cur.x][cur.y] = -1;//将到过的位置设置为-1,防止重复穷举

	s.push( cur);
	while (!s.empty())
	{
		pointer top = s.top();
		if (top.x == 8 && top.y == 8)
		{
			return true;//判断是否是终点,是则成功并返回true
		}

		int direction = getDirection(maze,top);//根据栈顶拿到下一步方向direction

		if (direction == 4)//direction == 4说明无路可走
		{
			s.pop();//无路可走出栈,根据前一个结点找新路径
		}
		else
		{
			top.x += direct[direction].incX;
			top.y += direct[direction].incY;//根据方向更新当前坐标
			maze[top.x][top.y] = -1;//将到过的位置设置为-1,防止重复穷举
			s.push(top);
		}
	}
	return false;
}

5. 代码总测试

输出路径对应的坐标(逆序,栈先进后出)

image-20221019152254466
#include<iostream>
#include<Stack>
using namespace std;

//定义方向结构体
struct Direction
{
	int incX, incY;
};
//定义存储四个结构体的数组,每个元素对应一个方向
Direction direct[4] = { {0,-1},{0,1},{-1,0},{1,0} };
//定义迷宫数组的长宽M,N
const int M = 8;
const int N = 8;

//迷宫中坐标
struct pointer
{
	int x, y;//表示迷宫中位置的横纵坐标
};

//根据当前结点获得下一步方向,返回整数temp,对应direct[temp]即为方向。若返回值则说明无路可走为
int getDirection(int** maze, pointer cur)
{
	int index = 0;
	while (index < 4)
	{
		//判断迷宫中direct[index]对应的方向是否可以走,可以则返回index作为下一步方向标识
		if (cur.x + direct[index].incX<N+1
			&& cur.y + direct[index].incY<M+1
			&&maze[cur.x + direct[index].incX][cur.y + direct[index].incY] == 0)return index;
		index++;
	}
	return 4;
}

//求解迷宫的函数,如果迷宫有解,就返回true。如果没解就返回false
bool findPath(int** maze, stack<pointer>& s)
{
	pointer cur;
	cur.x = 1;
	cur.y = 1;//cur就是起点
	maze[cur.x][cur.y] = -1;//将到过的位置设置为-1,防止重复穷举

	s.push( cur);
	while (!s.empty())
	{
		pointer top = s.top();
		if (top.x == 8 && top.y == 8)
		{
			return true;//判断是否是终点,是则成功并返回true
		}

		int direction = getDirection(maze,top);//根据栈顶拿到下一步方向direction

		if (direction == 4)//direction == 4说明无路可走
		{
			s.pop();
		}
		else
		{
			top.x += direct[direction].incX;
			top.y += direct[direction].incY;//根据方向更新当前坐标
			maze[top.x][top.y] = -1;//将到过的位置设置为-1,防止重复穷举
			s.push(top);
		}
	}
	return false;
}

int main()
{

	//定义并初始化迷宫二维数组
	int mazeSample[M + 2][N + 2] =
	{
		{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}
	};

	int** maze = (int**)malloc(sizeof(int*) * (M+2));
	for (int i = 0; i < M+2; i++)
	{
		maze[i] = (int*)malloc(sizeof(int) * (N+2));
	}
	for (int i = 0; i < M+2; i++)
	{
		for (int j = 0; j < N+2; j++)
		{
			maze[i][j] = mazeSample[i][j];
		}
	}
	for (int i = 0; i < M+2; i++)
	{
		for (int j = 0; j < N+2; j++)
		{
			cout << maze[i][j]<<" ";
		}
		cout << endl;
	}
	stack<pointer> storage;
	findPath(maze, storage);
	cout << endl;
	int num = 0;
	while (!storage.empty())
	{
		pointer p = storage.top();
		cout << "(" << p.x << "," << p.y << ")";
		num++;
		if (num % 4==0)cout << endl;
		storage.pop();
	}
	
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值