"栈"应用——求解迷宫

image

迷宫的数字表示:0表示通道,1表示墙,inP表示入口坐标,outP表示出口,

如图入口坐标为(1,1)出口为(8,8)

static int item[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}

};

static const POS inPos = {1,1},outPos ={8,8};


计算方法:若当前位置可通,则纳入当前路径,做标记(2)并继续朝下一个位置探索,即切换下一位置为当前位置

如此重复直至道道出口;

若当前位置不可通,则应顺着来的方向退回前一通道快,然后朝着除来向之外的其它方向继续搜索;

若该通道快四周4个方向均不可通,则应从当前路径上删除该通道块.

imageimage
假设从(4,1)开始,首先探测左边位置,不行

然后探测向下位置,发现可通,则把向下位置作为当前位置,(5,1)做压栈.

image

以(5,1)为当前位置,进行探测

imageimage

发现向下方向可通,则将(6,1)入栈,同样的(7,1)入栈

image

然后,发现其它三个方向都不可通,标记为走不通(3),且(上)这个位置被标记为(2)已走过,所以(7,1)退栈,退回到(6,1),(6,1)退栈,将方向转变为下一个方向进行压栈。(即寻找下一个路口)
image

然后我们还是发现,第三个位置(右边)走不通,上面(5,1)被标记为已走过(2),所以退栈,

image

(5,1)退栈,搜索四个方向,发现左边不可通,下面(6,1)走过,且四个方向都不通,已标记为(3),开始搜索第三个方向(右侧)(5,2)可通,把(5,2)当作当前位置,压栈。然后继续如此迭代下去。

下面可以看下数据结构的定义:

data.h

#ifndef _DATA_H
#define _DATA_H

//typedef int ElemType;

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

//栈的类型
typedef struct
{
	int sno;	//编号
	POS seat;	//位置
	int	di;		//朝向
}ElemType;

#endif


接下来的代码也贴下:

main.h

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<conio.h>
#include"stack.h"


//用二维数组构造迷宫的图形
static int item[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}

};

//定义迷宫的位置、出口,作为成功的判断依据。
static const POS inPos = {1,1},outPos ={8,8};


//判断是否可通,可通=0,不可通=1
int IsPass(POS curP){
	return item[curP.y][curP.x] == 0 ? 1 : 0;
}

//获得下一个方向,di=0,1,2,3分别代表左下右上
POS NextPos(POS curP,int di)
{
	POS p = curP;
	switch(di)
	{
		case 0:
			p.x--;
			break;
		case 1:
			p.y++;
			break;
		case 2:
			p.x++;
			break;
		case 3:
			p.y--;
	}
	return p;
	
}

//测试打印
void PrintItem(POS curP)
{
	int i,j;
	//清屏
	system("cls");
	for(i=0;i<10;i++)
	{
		for(j=0;j<10;j++)
		{
			//打印当前位置
			if(i == curP.y && j == curP.x)
			{
				printf("@");
				continue;
			}
			// *代表墙,空格代表可通
			if(item[i][j] == 1)
				printf("*");
			else
				printf(" ");
		}
		printf("\n");
	}
}

void main()
{
	ElemType e;
	int setp = 1;
	POS curPos = inPos;
	STACK *s = InitStack();

	PrintItem(inPos);
	getch();

	do
	{
		//1.判断当前位置是否可通
		if(IsPass(curPos))
		{
			//存当前步到e
			e.sno = setp;
			//它的方向
			e.di = 0;
			//他的坐标
			e.seat = curPos;
			Push(s,&e);
			//留下走过的足迹2代表走过
			item[curPos.y][curPos.x] = 2;
			//判断当前位置是否是出口。
			if(curPos.y == outPos.y && curPos.x == outPos.x)
			{
				PrintItem(curPos);
				//找到了
				printf("OK");
				break;
			}
			//打印当前位置
			PrintItem(curPos);
			getch();
			//找下一个位置
			curPos = NextPos(curPos,0);
			//把它的步骤递增
			setp++;
			
		}else{
			Pop(s,&e);
			//如果节点每个方向找完都没通,且栈中还有元素就返回上一个
			while(e.di == 4 && !IsEmpty(s))
			{
				//把都不能走通的位置做一个标志
				item[curPos.y][curPos.x] = 3;
				Pop(s,&e);
			}
			if(e.di < 3)
			{
				//方向指向它的下一个
				e.di++;
				//重新压栈(原来的位置,下一个方向)
				Push(s,&e);
				curPos = NextPos(e.seat,e.di);
			}
		}
	}while(!IsEmpty(s));
}



分析下算法。


do
{
	if(若当前位置可通)
	{
		将当前位置插入栈顶
		若该位置是出口,则结束
		否则切换当前位置的第一方向方块为当前位置
	}else{
		若栈不空切栈顶位置尚有其它方向未经探索
			则取栈顶方块的下一方向的相邻方块为当前方块
		若栈不空且栈顶位置的四周均不可通则
		{
			删去栈顶位置;
			若栈不空,则重新测试新的栈的位置,直到找到
			一个可通的相邻块或出栈至栈空
		}
	}
}while(栈不空);

思成老师引入了#include<conio.h>在代码中插入getch()使输出暂停

引入了#include<stdlib.h>通过函数system(“cls”)清屏,使得每找到一个可通的位置时,或者从不可通的路径退回到这个位置时都显示一张图。并且以@表示当前位置。

原文地址:https://www.cnblogs.com/shenerguang/p/2330687.html