数据结构(7)—栈的应用之迷宫求解

参考书籍:数据结构(C语言版)严蔚敏吴伟民编著清华大学出版社

本文中的代码可从这里下载:https://github.com/qingyujean/data-structure

1.问题描述:

采用穷举求解方法,从入口出发,顺某一方向向前探索,若能走通,则继续往前走;否则沿原路返回,换一个方向再继续探索。为了保证在任何位置上都能沿原路返回,显然需要用一个后进先出的栈结构来保存从入口到当前位置的路径。

        迷宫如图所示:

    图中红色方块表示“墙壁”是无法通过的,蓝色方块是通道块是可以通过的。所求路径必须是简单路径,即在求的的路径上不得重复出现同一通道块。

假设“当前位置”指的是”在搜索过程中某一时刻所在迷宫图中的某个方块的位置“,则求迷宫的一条路径的算法的基本思想似乎:

1.若当前位置”可通“,则纳入”当前路径”,并继续朝”下一位置探索“,即切换”下一位置“为”当前位置“,如此重复直到到达出口;

2.若当前位置”不可通“,则应顺着”来向“退回到”前一通道块“。然后朝着除”来向“之外的其他方向继续探索;

3.若该通道块的四周方块均”不可通“,则应从”当前路径“上删除该通道块。

所谓”下一位置“指的是”当前位置“的四周4个方向上(东、南、西、北)相邻的方块。假设以栈S记录”当前路径“,则栈顶存放的是”当前路径上最后一个通道块“,由此,”纳入路径“的操作即为”当前位置入栈“;”从当前路径上删除前一通道块“的操作即为”出栈“。

2.算法描述

求迷宫中一条从入口到出口的路径的算法可简单描述如下:

        设当前位置的初值为入口位置;

        do{

        若当前位置可通,

        则{

                 将当前位置压至栈顶; // 纳入路径

                 如果当前位置是出口位置,则结束;

                 否则切换当前位置的东邻方块为新的当前位置;

}

否则,

        若栈不为空且栈顶位置尚有其他方向未经探索,

        则设定新的当前位置为沿顺时针方向旋转找到的栈顶位置的下一相邻方块;

若栈不为空且栈顶位置的四周均不可通,

则{

        删去栈顶位置;

        若栈不为空,则重新测试新的栈顶位置,直至找到一个可通的相邻块或者出栈至栈空;

}

}while(栈不为空);

上图迷宫所求的路径应该如下图所示:

3.代码实现

用到的栈的相关操作如下:

typedef struct SqStack{
 SElemType data[MAXSIZE];
 int top;//指向栈顶元素
}SqStack;

 

//初始化空栈
void initStack(SqStack &s)

//判栈空
bool isEmpty(SqStack s)

//判栈满
bool isFull(SqStack s)

//入栈
void push(SqStack &s, SElemType e)

//出栈
void pop(SqStack &s, SElemType &e)

他们的具体实现参见我的另一篇博客:数据结构(5)--栈的定义以及相关操作的实现http://blog.csdn.net/u010366748/article/details/50639195

 

#include<stdio.h>
#define mazeRowNum 10//迷宫行数
#define mazeColNum 10//迷宫列数
#define MAXSIZE 100//栈大小

//迷宫中的坐标位置
typedef struct{
	int x;//行号
	int y;//列号
}PosType;

//栈的元素类型
typedef struct{
	//int ord;//通道块在路径上的“序号”
	PosType seat;//通道块在迷宫中的“坐标位置”
	int direction;//从此通道块走向下一通道块的方向,di=1,2,3,4分别表示东,南,西,北
}SElemType;

 

//定义迷宫,' '表示通道块,'#'表示墙壁,在后面的执行过程中,迷宫的元素可能变成'*'表示路径,'@'表示曾经走过但是无法到达出口
static char maze[mazeRowNum][mazeRowNum] = {
	{'#', '#', '#', '#', '#', '#', '#', '#', '#', '#'},
	{'#', ' ', ' ', '#', ' ', ' ', ' ', '#', ' ', '#'},
	{'#', ' ', ' ', '#', ' ', ' ', ' ', '#', ' ', '#'},
	{'#', ' ', ' ', ' ', ' ', '#', '#', ' ', ' ', '#'},
	{'#', ' ', '#', '#', '#', ' ', ' ', ' ', ' ', '#'},
	{'#', ' ', ' ', ' ', '#', ' ', ' ', ' ', ' ', '#'},
	{'#', ' ', '#', ' ', ' ', ' ', '#', ' ', ' ', '#'},
	{'#', ' ', '#', '#', '#', ' ', '#', '#', ' ', '#'},
	{'#', '#', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '#'},
	{'#', '#', '#', '#', '#', '#', '#', '#', '#', '#'}
};

PosType start = {1, 1};//设置迷宫起点
PosType end = {8, 8};//设置迷宫终点
//对曾经走过的通道块留下痕迹,以防止所求路径不是简单路径
void footPrint(PosType curpos){
	maze[curpos.x][curpos.y] = '*';//表示到达了该通道块
}
//曾走过的通道块但是无法到达出口,是“不通的”路,标记以免陷入“死胡同”
void markPrint(PosType curpos){
	maze[curpos.x][curpos.y] = '@';//表示该通道块虽然不是墙壁,但是它仍然不通
}

 

PosType nextPos(PosType curpos, int direction){
	switch(direction){
		case 1: curpos.y++;break;//向东走,行号不变,列号加1
		case 2: curpos.x++;break;//向南走,行号加1,列号不变
		case 3: curpos.y--;break;//向西走,行号不变,列号减1
		case 4: curpos.x--;break;//向北走,行号减1,列号不变
	}
	return curpos;
}
//判断当前位置是否可通,即为' ',而不是'#'、'*'(已走过)、'@'(已走过但不通)
bool pass(PosType curpos){
	if(maze[curpos.x][curpos.y] == ' ')
		return true;
	else
		return false;
}
//若迷宫中存在从入口start到出口end的通道,则求得一条路径存放在栈中(从栈底到栈顶),并返回TRUE,否则返回FALSE
bool mazePath(PosType start, PosType end){
	SqStack s;
	initStack(s);
	PosType curpos = start;//设定当前位置为“入口”位置
	//int curstep = 0;  //探索的第一步,用于表示路径序号
	do{
		if(pass(curpos)){//当前路径可通(是未曾到达过的通道块)
			footPrint(curpos);//留下"到此一游"的标记,为了求得的路径是简单路径
			//SElemType e = {curpos, 1};
			SElemType e;
			e.seat = curpos;
			e.direction = 1;
			push(s, e);  //加入路径
			if(curpos.x == end.x && curpos.y == end.y)  //到达出口
				return true;
			curpos = nextPos(curpos, 1);//下一位置是当前位置的东边
			//curstep++;   //探索下一步
		}else{//当前位置不能通过,则栈顶元素出栈,因为栈顶位置是当前位置的“来向”通道块,即当前位置的前一个位置
			SElemType e;
			pop(s, e);
			//如果弹出的栈顶位置的四周均不可通,则继续往“来路”通道块回退
			while(e.direction == 4 && !isEmpty(s)){
				markPrint(e.seat);//标记此通道块已经走过且不可通,标记是为了避免陷入死胡同
				pop(s, e);
			}
			//弹出的栈顶位置尚有其他方向的方块未探索,则切换到下一个方向的方块为当前位置
			if(e.direction < 4){
				e.direction++;
				push(s, e);
				curpos = nextPos(e.seat, e.direction);
			}
		}//end else
	}while(!isEmpty(s));//栈不为空则循环继续
	return false;
}
//打印迷宫
void printMaze(){
	for(int i = 0; i < mazeRowNum; i++){
		for(int j = 0; j < mazeColNum; j++){
			printf("%c ", maze[i][j]);
		}
		printf("\n");
	}
	printf("\n");
}
//仅打印迷宫中的路径
void printPath(){
	for(int i = 0; i < mazeRowNum; i++){
		for(int j = 0; j < mazeColNum; j++){
			if(i == 0 || j == 0 || i == mazeRowNum-1 || j == mazeColNum-1 || maze[i][j] == '*'){
				printf("%c ", maze[i][j]);
			}else{
				printf("  ");
			}
		}
		printf("\n");
	}
	printf("\n");		
}

4.演示

int main(){
	printf("迷宫的初始状态:\n");
	printMaze();
	if(mazePath(start, end)){
		printf("存在通路!\n");
		printf("迷宫的现态:\n");
		printMaze();
		printf("迷宫里的路径:\n");
		printPath();
	}else
		printf("不存在通路!\n");	

    return 0;	
}



 

本文中的代码可从这里下载:https://github.com/qingyujean/data-structure

评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值