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

  上篇文章我们讨论了,迷宫问题的普通求解问题,这篇文章我们继续深入,求迷宫的最短路径.

  要想求迷宫的最短路径,一个很简单的方法就是再设置一个Min栈,用来放最短路径,每找到一个出口,就将path栈与Min栈进行比较,如果path栈更小,则赋值给Min.

  而在上篇文章中,我们将走过的路径做了标记,每走一个坐标,就把那个坐标置为3,直至找到出口.

  因此如果用这种标记方式,显然是会出现问题的.

  所以我们需要换种标记方式!

  最终....我决定,使出口的值为2,每走一步使当前位置标记变为是上一位置标记再加1.

  在这种情况下,我们检测当前位置是否可以通过的函数(CheckIsAccess(int*,sz,Pos))就需要做一些微小的调整....

检测通路函数

/*
*函数功能:检测当前路径是否可以通过(最短路径)
*参数说明:
*    Maze:迷宫数组
*      sz:迷宫大小
*     cur:当前位置坐标
*    next:下一位置坐标
* 返回值:可以通过返回true,不能通过返回false
*/
bool CheckIsAccess(int *Maze,size_t sz,Pos cur,Pos next){
	//如果下一步路径越界
	if((next.x<0||next.x>sz)||
		(next.y<0||next.y>sz)){
		return false;
	}
	//下一坐标为0
	if(0 == (Maze[next.x*sz+next.y])){
		return true;
	}
	//下一坐标为之前走过的路
	if((Maze[next.x*sz+next.y]>Maze[cur.x*sz+cur.y]+1)){
		return true;
	}
	return false;
}

寻找最短路径函数

/*函数功能:求迷宫最短路径
*参数说明:
*    Maze:迷宫数组
*      sz:迷宫大小
*   entry:迷宫入口
*    path:走过的路径
*     Min:最短路径
* 返回值:可以通过返回true,不能通过返回false
*/
//求最短路径
void GetMazeMinPath(int *Maze,size_t sz,Pos &entry,stack<Pos>& path,stack<Pos> &Min){
	path.push(entry);
	Pos cur = entry;
	Pos next = cur;
	//找到出口
	if(sz-1 == cur.y){
		//第一次赋值给Min 或者path路径比Min短
		if(Min.empty()||path.size()<Min.size()){
			Min = path;
		}
		path.pop();
		return ;
	}
	//右
	next.x += 1;
	if(CheckIsAccess(Maze,sz,cur,next)){
		Maze[next.x*sz+next.y] = Maze[cur.x*sz+cur.y]+1;
		GetMazeMinPath(Maze,sz,next,path,Min);
	}
	//左
	next = cur;
	next.x -= 1;
	if(CheckIsAccess(Maze,sz,cur,next)){
		Maze[next.x*sz+next.y] = Maze[cur.x*sz+cur.y]+1;
		GetMazeMinPath(Maze,sz,next,path,Min);
	}
	//上
	next = cur;
	next.y += 1;
	if(CheckIsAccess(Maze,sz,cur,next)){
		Maze[next.x*sz+next.y] = Maze[cur.x*sz+cur.y]+1;
		GetMazeMinPath(Maze,sz,next,path,Min);
	}
	//下
	next = cur;
	next.y -= 1;
	if(CheckIsAccess(Maze,sz,cur,next)){
		Maze[next.x*sz+next.y] = Maze[cur.x*sz+cur.y]+1;
		GetMazeMinPath(Maze,sz,next,path,Min);
	}
	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(3);
		for(size_t i=0; i<sz; ++i){
			for(size_t j=0; j<sz; ++j){
				cout<<Maze[i*sz+j]<<setw(3);
			}
			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();
	}
	/*
	*函数功能:检测当前路径是否可以通过(最短路径)
	*参数说明:
	*    Maze:迷宫数组
	*      sz:迷宫大小
	*     cur:当前位置坐标
	*    next:下一位置坐标
	* 返回值:可以通过返回true,不能通过返回false
	*/
	bool CheckIsAccess(int *Maze,size_t sz,Pos cur,Pos next){
		//如果下一步路径越界
		if((next.x<0||next.x>sz)||
			(next.y<0||next.y>sz)){
			return false;
		}
		//下一坐标为0
		if(0 == (Maze[next.x*sz+next.y])){
			return true;
		}
		//下一坐标为之前走过的路
		if((Maze[next.x*sz+next.y]>Maze[cur.x*sz+cur.y]+1)){
			return true;
		}
		return false;
	}
	/*
	*函数功能:求迷宫最短路径
	*参数说明:
	*    Maze:迷宫数组
	*      sz:迷宫大小
	*   entry:迷宫入口
	*    path:走过的路径
	*     Min:最短路径
	* 返回值:可以通过返回true,不能通过返回false
	*/
	//求最短路径
	void GetMazeMinPath(int *Maze,size_t sz,Pos &entry,stack<Pos>& path,stack<Pos> &Min){
		path.push(entry);
		Pos cur = entry;
		Pos next = cur;
		//找到出口
		if(sz-1 == cur.y){
			//第一次赋值给Min 或者path路径比Min短
			if(Min.empty()||path.size()<Min.size()){
				Min = path;
			}
			path.pop();
			return ;
		}
		//右
		next.x += 1;
		if(CheckIsAccess(Maze,sz,cur,next)){
			Maze[next.x*sz+next.y] = Maze[cur.x*sz+cur.y]+1;
			GetMazeMinPath(Maze,sz,next,path,Min);
		}
		//左
		next = cur;
		next.x -= 1;
		if(CheckIsAccess(Maze,sz,cur,next)){
			Maze[next.x*sz+next.y] = Maze[cur.x*sz+cur.y]+1;
			GetMazeMinPath(Maze,sz,next,path,Min);
		}
		//上
		next = cur;
		next.y += 1;
		if(CheckIsAccess(Maze,sz,cur,next)){
			Maze[next.x*sz+next.y] = Maze[cur.x*sz+cur.y]+1;
			GetMazeMinPath(Maze,sz,next,path,Min);
		}
		//下
		next = cur;
		next.y -= 1;
		if(CheckIsAccess(Maze,sz,cur,next)){
			Maze[next.x*sz+next.y] = Maze[cur.x*sz+cur.y]+1;
			GetMazeMinPath(Maze,sz,next,path,Min);
		}
		path.pop();
	}
}
#endif

测试代码

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

总结:这块东西,还是蛮需要深入研究的,尤其对于那些对递归程序理解的不够好的同学,建议,一步一步的跟入程序,完成整个过程.当然求迷宫最短路径,这显然不是唯一的办法,更多的方法,以后再讨论吧!

附上工程文件:http://pan.baidu.com/s/1eSJr1YE(VS2010)

时间: 2024-08-06 03:51:11

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

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

本文适合于对迷宫问题已有初步研究,或阅读代码能力较强的人. 因此,如果你对迷宫问题一无所知,请参考其他更详细的资料. 迷宫问题,是一个对栈(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 问题分析 为了保证在任何位置上都能够退回原路,显然需要使用一个先进后出的数据结构来保存已经探寻过的位置,因此在程序求解迷宫路径的过程中采用栈这种数据结构. 迷宫是一个二维地图,其中含有出口和入口,障碍

迷宫问题求解——C++

迷宫问题思路 根据昨天的博客,有如下几种解决方案 克鲁斯卡尔 ,为避免死循环,需要设定优化路径的次数. Prim,为避免死循环,需要设定优化路径的次数,暂定200次. BFS , 实现简单,无死循环. DFS , 实现简单,无死循环,复杂度较低. 动态规划,实时根据权重调整方向,目前看来最合适的解决方案.需要以数据证明. 综上,本次优先选择BFS,首先不存在死循环的风险,其次算法复杂度较低,容易理解且实现.适合初步练手. 一. 思路及程序算法 首先建立迷宫,将其看作点位矩阵,先把墙堆起来,剩下的

42. 蛤蟆的数据结构笔记之四十二图的遍历之广度优先

42. 蛤蟆的数据结构笔记之四十二图的遍历之广度优先 本篇名言:"生活真象这杯浓酒 ,不经三番五次的提炼呵 , 就不会这样一来可口 ! -- 郭小川" 继续看下广度优先的遍历,上篇我们看了深度遍历是每次一个节点的链表是走到底的. 欢迎转载,转载请标明出处:http://write.blog.csdn.net/postedit/47029275 1.  原理 首先,从图的某个顶点v0出发,访问了v0之后,依次访问与v0相邻的未被访问的顶点,然后分别从这些顶点出发,广度优先遍历,直至所有的

数据结构实验之图论二:基于邻接表的广度优先搜索遍历

数据结构实验之图论二:基于邻接表的广度优先搜索遍历 Time Limit: 1000ms   Memory limit: 65536K  有疑问?点这里^_^ 题目描述 给定一个无向连通图,顶点编号从0到n-1,用广度优先搜索(BFS)遍历,输出从某个顶点出发的遍历序列.(同一个结点的同层邻接点,节点编号小的优先遍历) 输入 输入第一行为整数n(0< n <100),表示数据的组数. 对于每组数据,第一行是三个整数k,m,t(0<k<100,0<m<(k-1)*k/2,

SDUT 2142 【TEST】数据结构实验之图论二:基于邻接表的广度优先搜索遍历

数据结构实验之图论二:基于邻接表的广度优先搜索遍历 Time Limit: 1000MS Memory Limit: 65536KB Submit Statistic Discuss Problem Description 给定一个无向连通图,顶点编号从0到n-1,用广度优先搜索(BFS)遍历,输出从某个顶点出发的遍历序列.(同一个结点的同层邻接点,节点编号小的优先遍历) Input 输入第一行为整数n(0< n <100),表示数据的组数.对于每组数据,第一行是三个整数k,m,t(0<

SDUT 3341 数据结构实验之二叉树二:遍历二叉树

数据结构实验之二叉树二:遍历二叉树 Time Limit: 1000MS Memory Limit: 65536KB Submit Statistic Problem Description 已知二叉树的一个按先序遍历输入的字符序列,如abc,,de,g,,f,,, (其中,表示空结点).请建立二叉树并按中序和后序的方式遍历该二叉树. Input 连续输入多组数据,每组数据输入一个长度小于50个字符的字符串. Output 每组输入数据对应输出2行:第1行输出中序遍历序列:第2行输出后序遍历序列

SDUT 3399 数据结构实验之排序二:交换排序

数据结构实验之排序二:交换排序 Time Limit: 1000MS Memory Limit: 65536KB Submit Statistic Problem Description 冒泡排序和快速排序都是基于"交换"进行的排序方法,你的任务是对题目给定的N个(长整型范围内的)整数从小到大排序,输出用冒泡和快排对这N个数排序分别需要进行的数据交换次数. Input 连续多组输入数据,每组数据第一行给出正整数N(N ≤ 10^5),随后给出N个整数,数字间以空格分隔. Output

SDUT 3374 数据结构实验之查找二:平衡二叉树

数据结构实验之查找二:平衡二叉树 Time Limit: 400MS Memory Limit: 65536KB Submit Statistic Problem Description 根据给定的输入序列建立一棵平衡二叉树,求出建立的平衡二叉树的树根. Input 输入一组测试数据.数据的第1行给出一个正整数N(n <= 20),N表示输入序列的元素个数:第2行给出N个正整数,按数据给定顺序建立平衡二叉树. Output 输出平衡二叉树的树根. Example Input 5 88 70 61