迷宫问题求解(一)利用栈与递归求解出口

  本文适合于对迷宫问题已有初步研究,或阅读代码能力较强的人.

  因此,如果你对迷宫问题一无所知,请参考其他更详细的资料.

  迷宫问题,是一个对栈(Stack)典型应用的例子之一.

  假如,有如下10X10的迷宫(0代表通路,1代表障碍),我们需要用写程序来找出迷宫的出口.

1 1 1 1 1 1 1 1 1 1
1 1 1 0 1 1 1 0 1 1
0 0 0 0 1 0 0 0 1 1
1 1 0 1 1 0 1 0 0 1
1 1 0 1 0 0 1 0 1 1
1 1 0 1 1 1 1 0 0 1
1 1 0 0 0 0 0 0 1 1
1 1 0 1 0 1 1 0 1 1
1 1 0 1 0 1 1 0 1 1
1 1 1 1 1 1 1 0 1 1

那么,我们可以通过两种方式完成.

方式一:通过利用栈FILO(First In Last Out)的特性

核心代码

/*
*函数说明:通过栈来进行迷宫求解
*参数说明:
*	Maze:迷宫地图数组
*	  sz:迷宫大小
*      entry:迷宫入口点
*	path:用于寻找迷宫出口的栈
*返回值:找到出口返回true,没找到返回false.
*/
bool FindMazePath(int *Maze,size_t sz,Pos &entry,stack<Pos>& path){
	//将入口压栈
	path.push(entry);
	//如果栈不为空
	while(!path.empty()){
		//获取栈顶元素,即上一次走的路径
		Pos cur = path.top();
		//将其标记为已走过
		Maze[cur.x*sz+cur.y] = 3;
		//找到出口
		if(sz-1==cur.x){
			return true;
		}
		Pos next = cur;
		//下一步,向右移动
		next.x += 1;
		if(CheckIsAccess(Maze,sz,next)){
			//可以向右移动,将当前步入栈
			path.push(next);
			continue;
		}
		next = cur;
		//下一步,向左移动
		next.x -= 1;
		if(CheckIsAccess(Maze,sz,next)){
			//可以向左移动,入栈
			path.push(next);
			continue;
		}
		//下一步,向上移动
		next = cur;
		next.y += 1;
		if(CheckIsAccess(Maze,sz,next)){
			//可以向上移动
			path.push(next);
			continue;
		}
		next = cur;
		//向下移动
		next.y -= 1;
		if(CheckIsAccess(Maze,sz,next)){
			//可以向下移动
			path.push(next);
			continue;
		}
		//上、下、左、右都不能走
		path.pop();
	}
	return false;
}

方式二:通过递归

核心代码

/*
*函数说明:根据递归寻找迷宫出口
*参数说明
*	 Maze:迷宫地图
*	   sz:迷宫大小
*	entry:迷宫入口
*    path:用来判断是否存在出口的栈
*返回值:无(如果存在出口,栈为空;如果不存在出口,栈中存在起点坐标)
*/
void FindMazePathR(int *Maze,size_t sz,Pos &entry,stack<Pos> & path){
	//将入口压栈
	path.push(entry);
	Pos cur = entry;
	//将已走过的路标记为3
	Maze[cur.x*sz+cur.y] = 3;
	//找到出口,直接返回
	if(sz-1==entry.x){
		//将起点坐标弹出
		path.pop();
		return ;
	}
	Pos next = cur;
	//右
	next.x += 1;
	if(CheckIsAccess(Maze,sz,next)){
		//以当前位置为起点,递归进行下一步
		FindMazePathR(Maze,sz,next,path);
	}
	next = cur;
	//左
	next.x -= 1;
	if(CheckIsAccess(Maze,sz,next)){
		FindMazePathR(Maze,sz,next,path);
	}
	//上
	next = cur;
	next.y += 1;
	if(CheckIsAccess(Maze,sz,next)){
		FindMazePathR(Maze,sz,next,path);
	}
	//下
	next = cur;
	next.y -= 1;
	if(CheckIsAccess(Maze,sz,next)){
		FindMazePathR(Maze,sz,next,path);
	}
	path.pop();
}

最后,附上整个程序的完整代码(代码量较少,声明与实现我就不分文件了)

迷宫问题求解完整代码

//相关函数的声明与实现
#ifndef __MAZE_H__
#define __MAZE_H__
#include<iostream>
#include<iomanip>
#include<stack>
#include<assert.h>
namespace Maze{
	using namespace std;
	//迷宫大小
	static const int N = 10;
	//迷宫地图文件名
	static const char *const FILENAME = "MazeMap.txt";
	//坐标
	struct Pos{
		int x;	//横坐标(本质是数组arr[i][j]的j)
		int y;	//纵坐标(本质是数组arr[i][j]的i)
	};
	/*
	函数说明:从文件中获取迷宫地图
	参数说明:
		Maze:迷宫地图数组
		  sz:迷宫大小
	  返回值:无
	*/
	void GetMaze(int *Maze,size_t sz){
		FILE *fp = fopen(FILENAME,"r");
		//打开失败
		if(NULL==fp){
			//输出错误信息
			perror(FILENAME);
			//结束程序
			exit(1);
		}
		//将文件中的迷宫地图读入Maze数组内
		for(size_t i=0; i<sz; ++i){
			for(size_t j=0; j<sz;){
				//从文件流中获取字符
				char tmp = getc(fp);
				//字符为0或为1时,导入数组
				if(tmp==‘0‘||tmp==‘1‘){
					Maze[i*sz+j]=tmp -‘0‘;
					++j;
				}else if(EOF==tmp){
					//文件已读完,循环还未停止
					//说明此处文件中的迷宫地图存在问题
					assert(false);
					return ;
				}
			}
		}
		//关闭文件
		fclose(fp);
	}
	/*
	函数说明:打印迷宫
	参数说明:
		Maze:迷宫地图数组
		  sz:迷宫大小
	  返回值:无
	*/
	void PrintMaze(int *Maze,size_t sz){
		cout<<setw(2);
		for(size_t i=0; i<sz; ++i){
			for(size_t j=0; j<sz; ++j){
				cout<<Maze[i*sz+j]<<setw(2);
			}
			cout<<endl;
		}
	}
	/*
	函数说明:检测当前位置是否可以通过
	参数说明:
		Maze:迷宫地图数组
			sz:迷宫大小
		   cur:当前所在位置
	返回值:可以通过返回true,不能通过返回false.
	*/
	bool CheckIsAccess(int *Maze,size_t sz,Pos cur){
		if(cur.x>=0 && cur.x<sz &&		//行坐标是否越界
			cur.y>=0 && cur.y<sz &&		//列坐标是否越界
			Maze[cur.x*sz+cur.y]==0 ){	//所在行列是否可以通过
			return true;
		}
		return false;
	}
	/*
	函数说明:通过栈来进行迷宫求解
	参数说明:
		Maze:迷宫地图数组
			sz:迷宫大小
		entry:迷宫入口点
		path:用于寻找迷宫出口的栈
	返回值:找到出口返回true,没找到返回false.
	*/
	bool FindMazePath(int *Maze,size_t sz,Pos &entry,stack<Pos>& path){
		//将入口压栈
		path.push(entry);
		//如果栈不为空
		while(!path.empty()){
			//获取栈顶元素,即上一次走的路径
			Pos cur = path.top();
			//将其标记为已走过
			Maze[cur.x*sz+cur.y] = 3;
			//找到出口
			if(sz-1==cur.x){
				return true;
			}
			Pos next = cur;
			//下一步,向右移动
			next.x += 1;
			if(CheckIsAccess(Maze,sz,next)){
				//可以向右移动,将当前步入栈
				path.push(next);
				continue;
			}
			next = cur;
			//下一步,向左移动
			next.x -= 1;
			if(CheckIsAccess(Maze,sz,next)){
				//可以向左移动,入栈
				path.push(next);
				continue;
			}
			//下一步,向上移动
			next = cur;
			next.y += 1;
			if(CheckIsAccess(Maze,sz,next)){
				//可以向上移动
				path.push(next);
				continue;
			}
			next = cur;
			//向下移动
			next.y -= 1;
			if(CheckIsAccess(Maze,sz,next)){
				//可以向下移动
				path.push(next);
				continue;
			}
			//上、下、左、右都不能走
			path.pop();
		}
		return false;
	}
	/*
	*函数说明:根据递归寻找迷宫出口
	*参数说明
	*	 Maze:迷宫地图
	*	   sz:迷宫大小
	*	entry:迷宫入口
	*    path:用来判断是否存在出口的栈
	*返回值:无(如果存在出口,栈为空;如果不存在出口,栈中存在起点坐标)
	*/
	void FindMazePathR(int *Maze,size_t sz,Pos &entry,stack<Pos> & path){
		//将入口压栈
		path.push(entry);
		Pos cur = entry;
		//将已走过的路标记为3
		Maze[cur.x*sz+cur.y] = 3;
		//找到出口,直接返回
		if(sz-1==entry.x){
			//将起点坐标弹出
			path.pop();
			return ;
		}
		Pos next = cur;
		//右
		next.x += 1;
		if(CheckIsAccess(Maze,sz,next)){
			//以当前位置为起点,递归进行下一步
			FindMazePathR(Maze,sz,next,path);
		}
		next = cur;
		//左
		next.x -= 1;
		if(CheckIsAccess(Maze,sz,next)){
			FindMazePathR(Maze,sz,next,path);
		}
		//上
		next = cur;
		next.y += 1;
		if(CheckIsAccess(Maze,sz,next)){
			FindMazePathR(Maze,sz,next,path);
		}
		//下
		next = cur;
		next.y -= 1;
		if(CheckIsAccess(Maze,sz,next)){
			FindMazePathR(Maze,sz,next,path);
		}
		path.pop();
	}
}
#endif

迷宫求解测试代码

#include"Maze.h"
using namespace Maze;
void MazeTest(){
	int arr[N][N];		//迷宫地图
	Pos entry = {2,0};	//起点坐标
	stack<Pos> path;	//栈
	GetMaze((int *)arr,N);	//将文件中迷宫导入到arr数组中
	PrintMaze((int *)arr,N);//打印迷宫
	FindMazePath((int *)arr,N,entry,path);//找迷宫出口
	cout<<endl<<endl;		//换行处理(使界面更整齐)
	PrintMaze((int *)arr,N);//打印走过的迷宫
}
int main(){
	MazeTest();
	return 0;
}

总结:

  1.利用栈去寻找迷宫出口,栈内最终会保存从入口到出口的所有路径.

  2.利用递归去寻找迷宫出口,传进去的栈仅仅只是用来判断迷宫是否有出口,

  3.利用递归去寻找出口时,因为递归的特性,将会遍历完迷宫内的所有路径.

  最后,还有一个问题:如果一个迷宫存在多条路径可以到达出口,那么如何得到迷宫到出口的最短路径???

  有机会的话,我将会在下篇文章讨论此事.

  附上工程文件:http://pan.baidu.com/s/1gfoNrLD

时间: 2024-10-27 09:58:37

迷宫问题求解(一)利用栈与递归求解出口的相关文章

汉诺塔(Tower of Hanoi)问题的求解——利用栈与递归

汉诺塔(Tower of Hanoi)问题的求解--利用栈与递归 1. 汉诺塔问题的提法 汉诺塔问题是使用递归解决问题的经典范例. 传说婆罗门庙里有一个塔台,台上有3根标号为A.B.C的用钻石做成的柱子,在A柱上放着64个金盘,每一个都比下面的略小一点.把A柱上的金盘全部移到C柱上的那一天就是世界末日. 移动的条件是:一次只能移动一个金盘,移动过程中大金盘不能放在小金盘上面.庙里的僧人一直在移个不停,移动的最少总次数是264?1次,如果每秒移动一次的话,需要500亿年. 2. 求解汉诺塔问题的算

3.4.4 利用栈将递归转换成非递归的方法

在函数执行时系统需要设立一个“递归工作栈”存储第一层递归所需的信息,此工作栈是递归函数执行的辅助空间,所以可以看出,递归程序在执行时需要系统提供隐式栈这种数据结构来实现,对于一般的递归过程,仿照递归算法执行过程中递归工作栈的状态变化可直接写出相应的非递归算法.这种利用栈消除递归过程的步骤如下. (1)设置一个工作栈存放递归工作记录(包括实参.返回地址及局部变量等) (2)进入非递归调用入口(即被调用程序开始处)将调用程序传来的实在参数和返回地址入栈(递归程序不可以作为主程序,因而可认为初始是被某

利用栈非递归实现块排

递归实现块排:快速排序+随机快排 非递归实现块排具体思路如下图: # -*- coding:utf-8 -*- def quickSort(list): stack=[0] stack.append(len(list)-1) #利用栈存储下标 while stack: j=stack.pop() i=stack.pop() mid=sort(i,j,list) if mid>i+1: stack.append(i) stack.append(mid-1) if mid<j-1: stack.a

利用栈求解迷宫问题

利用栈求解迷宫问题 源代码: #include<stdio.h> #include<stdlib.h> #define M 8 #define N 8 #define MaxSize M*N typedef struct { int i;//当前方块的行号 int j;//当前方块的列号 int di; //di是下一个可走的相邻方块的方位号 }Box; typedef struct { Box data[MaxSize]; int top;      //栈顶指针 }StType

利用栈实现迷宫求解

利用栈实现迷宫求解 前言:众所周知,栈是(First in last out)先进后出的数据结构,利用这个属性可以实现类似与回溯的方式,比如当前数据满足条件,则入栈,否则出栈返回上一级,依次循环. 在本题中,将每个迷宫路径上的点封装成上下左右四个方向数节点,先入栈迷宫入口节点,如果上下左右没被使用,则将其上下左右的点入栈,否则出栈.如果最终达到迷宫终点则成功,否则失败. 如下是每个节点的数据结构 1 typedef struct{ 2 int top; 3 int bottom; 4 int l

数据结构应用:利用栈破解迷宫游戏

最近刚开始学数据结构,发现数据结构真是个神奇的东西哈,很多现实中的问题都可以用不同的数据结 构来解决,比如利用和栈中缀表达式编写一个计算机程序,利用栈破解迷宫游戏,今天我就来跟大家分 享一下如何利用栈来破解迷宫游戏. 学过数据结构的人都知道,栈的特点是:后进先出(First In Last Out);也就是说只能在栈的尾部进 行压栈和出栈,而且出栈的时候只能从最后一个数据开始.如下图: 而我们在破解迷宫游戏的时候采用的方法是"回溯",也就是在寻找通路的时候,每找到一个通路,就将这个数据

迷宫问题求解之“穷举+回溯”(一)(转载)

求迷宫从入口到出口的所有路径是一个经典的程序设计问题,求解迷宫,通常采用的是“穷举+回溯”的思想,即从入口开始,顺着某一个方向出发,若能够走通,就继续往前走:若不能走通,则退回原路,换一个方向继续向前探索,直到所有的通路都探寻为止.因此本文依据这种“穷举+回溯”的思想,设计一个求解迷宫的程序. 1 问题分析 为了保证在任何位置上都能够退回原路,显然需要使用一个先进后出的数据结构来保存已经探寻过的位置,因此在程序求解迷宫路径的过程中采用栈这种数据结构. 迷宫是一个二维地图,其中含有出口和入口,障碍

数据结构之迷宫问题求解(二)迷宫的最短路径

上篇文章我们讨论了,迷宫问题的普通求解问题,这篇文章我们继续深入,求迷宫的最短路径. 要想求迷宫的最短路径,一个很简单的方法就是再设置一个Min栈,用来放最短路径,每找到一个出口,就将path栈与Min栈进行比较,如果path栈更小,则赋值给Min. 而在上篇文章中,我们将走过的路径做了标记,每走一个坐标,就把那个坐标置为3,直至找到出口. 因此如果用这种标记方式,显然是会出现问题的. 所以我们需要换种标记方式! 最终....我决定,使出口的值为2,每走一步使当前位置标记变为是上一位置标记再加1

15、蛤蟆的数据结构笔记之十五栈的应用之栈与递归之八皇后问题

15.蛤蟆的数据结构笔记之十五栈的应用之栈与递归之八皇后问题 本篇名言:"人的一生应当这样度过:当回忆往事的时候,他不致于因为虚度年华而痛悔,也不致于因为过去的碌碌无为而羞愧:在临死的时候,他能够说:"我的整个生命和全部精力,都已经献给世界上最壮丽的事业--为人类的解放而斗争." 继续递归问题,本次是经典的八皇后问题: 欢迎转载,转载请标明出处: 1.  八皇后问题 八皇后问题,是一个古老而著名的问题,是回溯算法的典型案例.该问题是国际西洋棋棋手马克斯·贝瑟尔于1848年提出