栈结构之迷宫寻路

利用栈结构进行回退从而解决迷宫寻路问题

寻得的路径仅仅是一条可到达目的地的路径

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
//数组里面去寻路:位置--->行列
struct position
{
	int row;
	int cols;
};
struct position pathStack[100];		//栈内存(存放路径)
int stackTop = -1;					//栈顶标记
int** maze = NULL;					//二维数组去描述地图
int size = 0;						//迷宫的大小
int** makeArray(int row, int cols)  //二级指针指向二维数组
{
	int** array = (int**)malloc(sizeof(int*) * row);  
	for (int i = 0; i < cols; i++)
	{
		array[i] = (int*)malloc(sizeof(int) * cols);
	}
	return array;
}
//用户输入一个迷宫
void createMaze()
{
	printf("输入迷宫大小:");
	scanf("%d", &size);
	maze = makeArray(size + 2, size + 2);//加2表示边框
	printf("输入迷宫:\n");
	for (int i = 1; i <= size; i++)
	{
		for (int j = 1; j <= size; j++)
		{
			scanf("%d", &maze[i][j]);
		}
	}
	//加边框   1表示墙,不可以走
	for (int i = 0; i <= size + 1; i++)
	{
		//第一行和第size+2行
		maze[0][i] = 1;
		maze[size + 1][i] = 1;
		//第一列和第size+2列
		maze[i][0] = 1;
		maze[i][size + 1] = 1;
	}
}
//寻找路径
int findPath()
{
	 //偏移量描述出来
	struct position offset[4];     //0-3表示4个不同的方向
	//偏移量=>方向
	//往右边走
	offset[0].row = 0;
	offset[0].cols = 1;
	//往下边走
	offset[1].row = 1;
	offset[1].cols = 0;
	//往左边走
	offset[2].row = 0;
	offset[2].cols = -1;
	//往上边走
	offset[3].row = -1;
	offset[3].cols = 0;
	//等效 => struct position offset[4] = { {0,1},{1,0},{0,-1},{-1,0} };
	//选定入口
	struct position here = { 1,1 };//当前的位置坐标
	//走迷宫=>记录走过的路径
	//走过的地方标记为1
	maze[1][1] = 1;
	int option = 0;		//下一个移动方向
	int endOption = 3;	//终止方向
	while (here.row != size || here.cols != size)//终点位置为(size,size)
	{
		//未到达终点位置时,继续走
		int rowNum, colsNum;		//记录下标变化
		while (option <= endOption)
		{
			//行列变化 = 原位置 + 偏移值    (偏移值由方向决定)
			rowNum = here.row + offset[option].row;
			colsNum = here.cols + offset[option].cols;
			//一旦确定一个方向可以走,就需要去下一步
			if (maze[rowNum][colsNum]==0)
			{
				break;  //能走,退出循环,走到下一个位置
			}
			else//不能走,换方向测试
			{
				option++;
			}
		}
		//跳出上面while可能是有break跳出的(这时option<=endOption)   这种情况下有路可以走
		//也可以是由于option > endOption不再满足while循环条件造成的		这种情况下无路可走
		if (option <= endOption)//有路可以走
		{
			//走到下一个
			pathStack[++stackTop] = here;
			//改变当前位置
			here.row = rowNum;
			here.cols = colsNum;
			//走过的路径标记=>堵上
			maze[rowNum][colsNum] = 1;
			option = 0;		//起始方向置为零去找下一个位置
		}
		else//这时option > endOption(option等于4),无路可走,接下来要进行退步(回到上一步去)
		{
			//回到上一步去
			if (stackTop == -1) return 0;//栈为空,无路可走(没有路径)
			struct position next = pathStack[stackTop--];//出栈方式=>回退到上一步
			//方向的处理
			if (next.row == here.row)//行不变,左右走
			{
				option = (2 + next.cols - here.cols) % 4;
			}
			else//行发生变化,则列不变。上下变化。
			{
				option = (3 + next.row - here.row) % 4;
			}
			here = next;   //位置回退到上一个位置
		}
	}
	return 1;
}

//打印路径
void printPath()
{
	if (stackTop == -1)
	{
		printf("无路可走\n");
	}
	else
	{
		printf("路径方式:\n");
		struct position curPos;
		while (stackTop != -1)
		{
			curPos = pathStack[stackTop--];
			printf("(%d,%d)-->", curPos.row, curPos.cols);
		}
		printf("\n");
	}
}
int main()
{
	createMaze();
	if (findPath())	printPath();
	else printf("无路可走!\n");
	return 0;
}


思路:从起点开始,按着右、下、左、上的方向(偏移量)进行寻路。只要偏移后的位置的数值为零就过去。每到达一个位置,就将该位置入栈,并将该位置的二维数组的值设为1来标记已经走过。若一个点的四周都无路可走,则进行回退出栈。

核心:findPath();函数

方向的处理:

在这里插入图片描述
这里方向的处理不太容易理解:

当一个位置的四个方向都无路可走时(即该位置的四个方向都被标记为1),就需要进行回退出栈。

首先是位置的回退:将其回退到该位置的前一个位置。

其次是方向的回退:这时我们如果将方向option置为0并不会影响寻路的结果,但是会减小寻路的效率。

当我们将回退后的位置改为到达无路可走位置的方向的下一个方向时。效率更高,因为避免尝试了不可能通过的方向。

如下:
在这里插入图片描述

  • 当行不变时说明是左右移动的,由左往右移动时,next.cols - here.cols=-1,回退后的方向应该是向下为1;由右往左移动时,next.cols - here.cols=1,回退后的方向应该为3。综上考虑,回退后的方向应该为:option = (2 + next.cols - here.cols) % 4;

  • 行改变时,列就不发生变化。这时应该为上下移动。由下往上移动时,next.row - here.row=1,回退后的方向应该为0;由上向下移动时,next.row - here.row=-1,回退后的方向应该为2。综上所述,回退后的方向应该为:option = (3 + next.row - here.row) % 4;

#include using std::cout; using std::cin; #include #include #include #define OVERFLOW -2 #define INIT_SIZE 100 //存储空间初始分配量 #define INCREMENT 10 //存储空间分配增量 typedef struct{ int r; int c; }PostType;//迷宫中r行c列的位置 typedef struct{ int ord; //当前位置在路径上的序号 PostType seat;//当前坐标 int di; //往下一坐标的方向 }SElemType; //元素类型 typedef struct{ SElemType* base;//基址,构造前销毁后为空 SElemType* top;//顶 int stackSize; //容量 }Stack; //类型 int InitStack(Stack &S){ //构造空s S.base=(SElemType*)malloc(INIT_SIZE *sizeof(SElemType)); if(!S.base) exit(OVERFLOW);//存储分配失败 S.top=S.base; S.stackSize=INIT_SIZE; return 0; } int StackEmpty(Stack S)//若s为空返回TRUE,否则返回FALSE { if(S.top==S.base) return 1; return 0; } int Push(Stack &S,SElemType e){ //插入元素e为新的顶元素 if(S.top-S.base >=S.stackSize){//满,加空间 S.base=(SElemType *)realloc(S.base,(S.stackSize+INCREMENT)*sizeof(SElemType)); if(!S.base) exit(OVERFLOW); //存储分配失败 S.top=S.base+S.stackSize; S.stackSize+=INCREMENT; } *S.top++=e; return 1; } int Pop(Stack &S,SElemType &e){//若不空删除,顶元素用e返回 if(S.top==S.base) return 0; e=*--S.top; return 1; } int DestroyStack(Stack &S){//销毁S, free(S.base); S.top=S.base; return 1; } #define MAXLEN 16//迷宫包括外墙最大行列数目 typedef struct{ int r; int c; char adr[MAXLEN][MAXLEN]; }MazeType; //迷宫类型 int InitMaze(MazeType &maze){ //初始化迷宫若成功返回TRUE,否则返回FALSE int i,j; cout<>maze.r>>maze.c; //迷宫行和列数 for(i=0;i<=maze.c+1;i++){//迷宫行外墙 maze.adr[0][i]='#'; maze.adr[maze.r+1][i]='#'; } for(i=0;i<=maze.r+1;i++){//迷宫列外墙 maze.adr[i][0]='#'; maze.adr[i][maze.c+1]='#'; } for(i=1;i<=maze.r;i++) for(j=1;j<=maze.c;j++) maze.adr[i][j]=' ';//初始化迷宫 int m=1,n=maze.c; for(m;m1;n--) maze.adr[6][n]='#'; maze.adr[4][5]='#'; maze.adr[4][6]='#'; maze.adr[3][3]='#'; maze.adr[3][2]='#'; maze.adr[5][7]='#'; maze.adr[5][6]='#'; maze.adr[8][3]='#'; maze.adr[7][2]='#'; maze.adr[7][5]='#'; return 1; }//InitMaze int Pass(MazeType maze,PostType curpos){ if(maze.adr[curpos.r][curpos.c]==' ') return 1; else return 0; }//Pass int FootPrint(MazeType &maze,PostType curpos){ //若走过并且可通返回TRUE,否则返回FALSE //在返回之前销毁S maze.adr[curpos.r][curpos.c]='!';//"*"表示可通 return 1; }//FootPrint PostType NextPos(PostType &curpos,int i){ //指示并返回下一位置的坐标 PostType cpos; cpos=curpos; switch(i){ case 1 : cpos.c+=1; break; case 2 : cpos.r+=1; break; case 3 : cpos.c-=1; break; case 4 : cpos.r-=1; break; default: exit(0); } return cpos; }//Nextpos int MarkPrint(MazeType &maze,PostType curpos){ maze.adr[curpos.r][curpos.c]='@';//"@"表示曾走过但不通 return 1; } int MazePath(MazeType &maze,PostType start,PostType end){ //若迷宫maze存在从入口start到end的通道则求得一条存放在中 Stack S; PostType curpos; int curstep;//当前序号,1.2.3.4分别表示东,南,西,北方向 SElemType e; InitStack(S); curpos=start; //设置"当前位置"为"入口位置" curstep=1; //探索第一步 do{ if(Pass(maze,curpos)){//当前位置可以通过,即是未曾走到过的通道 FootPrint(maze,curpos);//留下足迹 e.ord=curstep; e.seat=curpos; e.di=1; Push(S,e); //加入路径 if(curpos.r==end.r&& curpos.c==end.c) if(!DestroyStack(S))//销毁失败 exit(OVERFLOW); else return 1; //到达出口 else{ curpos=NextPos(curpos,1); //下一位置是当前位置的东邻 curstep++; //探索下一步 } } else{ //当前位置不通 if(!StackEmpty(S)){ Pop(S,e); while(e.di==4 && !StackEmpty(S)){ MarkPrint(maze,e.seat); Pop(S,e); //留下不能通过的标记,并退一步 } if(e.di < 4){ e.di++;//换下一个方向探索 Push(S,e); curpos=NextPos(e.seat,e.di);//设定当前位置是该新方向上的相邻位置 } } } }while(!StackEmpty(S)); if(!DestroyStack(S))//销毁失败 exit(OVERFLOW); else return 0; }//MazePath void PrintMaze(MazeType &maze){ //将标记路径信息的迷宫输出 int i,j; cout<<"\n——!为所求迷宫路线路线——:\n\n"; cout<<" "; for(i=0;i<=maze.r+1;i++)//打印列数名 cout<<" "<<i; cout<<"\n\n"; for(i=0;i<=maze.r+1;i++){ cout<<" "<<i;//打印行名 for(j=0;j<=maze.c+1;j++) cout<<" "<<maze.adr[i][j];//输出迷宫路径 cout<<"\n\n"; } } void main(){ MazeType maze; PostType start,end; char cmd; do{ cout<<"-------建立迷宫--------\n"; if(!InitMaze(maze)){ cout<<"\n——建立有误——!!!\n"; exit(OVERFLOW); } do{ cout<>start.r>>start.c; if(start.r>maze.r || start.c>maze.c){ cout<maze.r || start.c>maze.c); do{ cout<>end.r>>end.c; if(end.r>maze.r || end.c>maze.c){ cout<maze.r || end.c>maze.c); if(!MazePath(maze,start,end)) cout<<"\n不能求得路径!\n"; else PrintMaze(maze); cout<>cmd; }while(cmd=='y' || cmd=='Y'); }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值