迷宫问题超详解(栈实现)

我给的建议是,如果你不是很理解这个问题的话,或者完全不懂,那么请进行默读或者朗读,并且边读边想
这里我在网上找了一个视频,有基本思路的讲解,看完可以看以下 的代码讲解
迷宫问题链接

在这里插入图片描述

我们将问题分块解释

#include<iostream>
const int M = 8, N = 8;
const int Maxsize = 200;
using namespace std;

int mg[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}
};
//运用1和0对不可以走和可以走的方块进行标志

typedef struct
{
    
    
	int i;         //当前方块的行号
	int j;         //当前方块的列号
	int di;        //di是下一可走相邻方位的方位号
}Box;              //定义方块类型

typedef struct
{
    
    
	Box data[Maxsize];   
	  //因为没走一步相当于进栈,并且要把那个方块的各个性质带到栈中
	  //所以用上面定义好了的BOX结构体类型来定义数组
	int top;      //栈顶指针
}StType;          //声明顺序栈类型

//初始时,入口(i,j)作为当前方块
//所有走过的方块都会进栈

走迷宫的过程跟进栈出栈一样,移动一个方位就进栈,走不动了,就原路退回,就相当于退栈,所以所以就想到用栈来解决问题。
在这里插入图片描述
Box中的di指的是方位,一开始进栈时,它是被初始化为-1的,这表示它还没有进行试探,还没有试探周围那一个方块可以走。
假设上面的方位为0,右为1,下为2,左为3
当开始试探时,就会对di进行赋值,比如上边可以走,那么di就为0,若上边走不通,则试右边,右边走得通,那么di就为1
(这些方位数、时针顺序等都是人为设定的,你也可以自己定义)

void InitStack(StType*& st)
{
    
    
	st = (StType*)malloc(sizeof(StType));
	st->top = -1;
}
bool StackEmpty(StType* s)
{
    
    
	return (s->top == -1);
}
bool Push(StType*& s, Box e)
{
    
    
	if (s->top == Maxsize - 1)
		return false;
	s->top++;
	s->data[s->top] = e;
	return true;
}
bool GetTop(StType* s, Box& e)
{
    
    
	if (s->top == -1)
		return false;
	e = s->data[s->top];
	return true;
}
bool Pop(StType*& s, Box& e)
{
    
    
	if (s->top == -1)
		return false;
	e = s->data[s->top];
	s->top--;
	return true;
}
void DestroyStack(StType*& s)
{
    
    
	free(s);
}

上面这些都是栈的基本操作,后面会用到
我准备了一个有注释的和没注释的代码

//用栈求一条迷宫路径的算法:(xi,yi),(xe,ye)
bool mgpath(int xi, int yi, int xe, int ye)
{
    
    
	Box path[Maxsize], e;
	int i, j, di, i1 = 0, j1 = 0, k;
	bool find;
	StType* st;
	InitStack(st);               //初始化栈顶指针
	e.i = xi; e.j = yi; e.di = -1;//设置e为入口
	Push(st, e);                //方块e进栈
	mg[xi][yi] = -1;
	while (!StackEmpty(st))
	{
    
    
		GetTop(st, e);
		i = e.i; j = e.j; di = e.di;
		if (i == xe && j == ye)
		{
    
    
			cout << "一条迷宫路径如下:" << endl;
			k = 0;
			while (!StackEmpty(st))
			{
    
    
				Pop(st, e);
				path[k++] = e;
			}
			while (k >= 1)
			{
    
    
				k--;
				cout <<"\t"<<path[k].i << path[k].j;
				if ((k + 2) % 5 == 0)
				cout << endl;
			}
			cout << endl;
			DestroyStack(st);
			return true;
		}
		find = false;
		while (di < 4 && !find)
		{
    
    
			di++;
			switch (di)
			{
    
    
			case 0:i1 = i - 1; j1 = j; break;
			case 1:i1 = i; j1 = j + 1; break;
			case 2:i1 = i + 1; j1 = j; break;
			case 3:i1 = i; j1 = j - 1; break;
			}
			if (mg[i1][j1] == 0)find = true;
		}
		if (find)
		{
    
    
			st->data[st->top].di = di;
			e.i = i1; e.j = j1; e.di = -1;
			Push(st, e);
			mg[i1][j1] = -1;
		}
		else
		{
    
    
			Pop(st, e);
			mg[e.i][e.j] = 0;
		}
	}
	DestroyStack(st);
	return false;
}

接下来将上述代码分段解释

bool mgpath(int xi, int yi, int xe, int ye)
{
    
    
	Box path[Maxsize], e;
	int i, j, di, i1 = 0, j1 = 0, k;
	bool find;
	StType* st;
	InitStack(st);               //初始化栈顶指针
	e.i = xi; e.j = yi; e.di = -1;//设置e为入口
	Push(st, e);                //方块e进栈
	mg[xi][yi] = -1;

在这里插入图片描述
一开始就是这么进栈的,这是第一个元素,并且要注意mg[xi][yi] = -1和di=-1是不同的,前者是表示将迷宫值,也就是开头定义的数组的值其中一个变为-1,让其不能走(只有迷宫值0是可以走的,其它都不可以走),避免走到下一个,又走回来(在有其它路的情况下),也就是为了避免来回循环
后者是表示未试探(上面已经说过)

while (!StackEmpty(st))          //上面有一个入口方块被压入,所以可以进行循环
	{
    
    
		GetTop(st, e);      //获取栈顶的元素
		i = e.i; j = e.j; di = e.di;      //将栈顶方块的元素赋给i,j,di
		if (i == xe && j == ye)           //判断是否坐标是出口
		{
    
    
			cout << "一条迷宫路径如下:" << endl;
			k = 0;
			while (!StackEmpty(st))           
			//不断将元素弹出,只不过最后走的那一步,是最为第一个进入数组path中的
			//所以要想要得到从入口到出口的顺序则需将path倒序输出
			{
    
    
				Pop(st, e);            
				path[k++] = e;
			}
			while (k >= 1)       //这里不是大于等于0,与k++有关,看下句注释即可理解
			{
    
    
				k--;            //前面k++每个都多加了一个1,所以每个都得减一
				cout <<"\t"<<path[k].i << path[k].j;
				if ((k + 2) % 5 == 0)    //这是经验值,因为只有在这个迷宫里面才是加二,看下面图片即可知
				cout << endl;
			}
			cout << endl;
			DestroyStack(st);
			return true;      
	 //完成输出后,因为它是个bool函数,而它的使命就是输出走出迷宫路径,使命完成了,自然就得return
		}

在这里插入图片描述
这是将这个例子的k值减减后一个个输出的结果,可以发现当k+2等于5的倍数时,就会换一次行,所以输出的是5个5个一行。

        find = false;          //假设找不到
		while (di < 4 && !find)    
		 //两个条件:在找不到的情况下,从0-3一个一个找
		 //这里的!find也可以在里面换成用break
		{
    
    
			di++;
			switch (di)
			{
    
    
			case 0:i1 = i - 1; j1 = j; break;
			case 1:i1 = i; j1 = j + 1; break;
			case 2:i1 = i + 1; j1 = j; break;
			case 3:i1 = i; j1 = j - 1; break;
			}
			if (mg[i1][j1] == 0)find = true;
			//发现如果下一个点位的迷宫值为0,则说明可走,即找到
		}
		if (find)    //如果找到的话,就进行进栈操作,而人也将移动至发现的这个方块
		{
    
    
			st->data[st->top].di = di;
			//st指向data,data要di,所以这个代码的意思就是st指向的栈顶数组元素的di
			e.i = i1; e.j = j1; e.di = -1;
			//把Box中的三项装入e中,后面即可压入栈中
			Push(st, e);
			mg[i1][j1] = -1;       //上面有解释过
		}
		else                    //如果不是记得要退栈,并且将退出去的那个元素赋值变回原来的0,这其实就是四面被堵死
		{
    
    
			Pop(st, e);
			mg[e.i][e.j] = 0;
		}
	}
	DestroyStack(st);
	return false;
}

用循环让其重复进栈出栈的过程,最后看是否能到达出口

int main()
{
    
    
	if (!mgpath(1, 1, M, N))
	//这里很坑
	//发现是return false的话就输出下面语句
	//如果是return true的话就不输出下面语句,而是会输出迷宫路径(mgth函数已经写好)
		cout << "该迷宫问题没有解!";
	return 1;
}

猜你喜欢

转载自blog.csdn.net/e2788666/article/details/115918714