模拟DFS深度寻路算法解决迷宫问题

题目与要求


题目描述

​ 以一个m×n的长方阵表示迷宫,0和1分别表示迷宫中的通路和障碍。设计一个程序,对任意设定的迷宫,求出所有从入口到出口的通路,或得出没有通路的结论。

设计要求

​ 编写一个求解迷宫的非递归和递归程序。求得的通路以三元组(i,j,d)的形式输出,其中:(i,j)指示迷宫中的一个坐标,d表示走到下一坐标的方向。如:对于下列数据的迷宫,输出的一条通路为z(1,1,1),(1,2,2),(2,2,2),(3,2,3),(3,1,2),······。

总体设计


设计概述

​ 经过查阅文献,翻阅资料可得知,本次课程设计所面临的问题,将选择DFS深度寻路算法和回溯算法加以实现。同时,我们这里也可以引入图形库,将程序设计成为一个自动走迷宫的程序运行效果,详细展现出迷宫寻路的可视化过程,帮助大家详细理解DFS深度寻路算法以及回溯法。


整体框架模块

在这里插入图片描述

详细设计


数据结构设计

数据结构选择:

​ 栈、图

类的定义:

​ 坐标点类。

图:

​ 本问题为迷宫问题,需要通过图来描述迷宫的路况,所以需要用图这一数据结构。

表示方法:

int map ROWS = {

			{1,1,1,1,1,1,1,1},

			{1,0,0,0,0,0,0,1},

			{1,0,1,0,1,0,1,1},

			{1,0,1,0,1,0,1,1},

			{1,0,1,1,1,0,1,1},

			{1,0,0,0,1,0,1,1},

			{1,0,0,0,0,0,0,1},

			{1,1,1,1,1,1,1,1}

		};

图将由二维数组(矩阵)来实现,里面充满0和1,0代表通路,1代表地图里面的障碍。

栈:

​ 在寻路过程中需要记录路径的遍历情况,并考虑到当走错路径时需要删除错误的路径,从而选择栈这一数据结构是非常合适的。

​ 表现形式:

stack<Point> St;

​ 调用C++中STL标准库里面的头文件直接定义一个有关Point类的栈,用于Point数据类型的压栈和出栈。

点Point:

​ 定义一个类Point,里面包含属性:行坐标x,列坐标y,方向指数dir(1:下;2:右;3:上;4:左)下一个要走的方向。

算法思想和模块设计

DFS深度寻路算法

​ 该算法从图的某个顶点出发,沿着一个路径遍历图直到最后一个顶点,并在回溯时继续搜索其他路径,直到所有节点都被访问为止。DFS 通过利用栈来记录每个节点的状态,从而实现对图或树的遍历。在算法实现中,DFS 通常使用递归方法或显式栈实现。

回溯算法

​ 该算法是一种用于求解决策问题的算法,它通过尝试所有可能的解决方案来找出最佳的解决方案。回溯算法通常适用于问题的解空间非常大,且没有直接的有效解法的情况。回溯算法按照深度优先的顺序尝试每一个可能的解决方案,如果当前的方案无法满足问题的约束条件,则回溯到上一个节点重新进行尝试。这个过程不断重复,直到找到问题的最终解或者确定不存在解决方案为止。


详细解题思路

​ 了解到两种算法的核心概念和思维后,那么我们就可以详细设计解题思路了。

​ 我们可以根据图深度优先搜索的算法思想,探索出一条起点到终点的路径,加上回溯,搜索出其他的路径,最终会在控制台上打印出所有的路径。

​ 然后我们要实现程序算法的可视化:我们不难得知在C/C++语言中,里面内置一种<graphics.h>,需要将easyX配置到VS2022的编译器上,方可运行使用。编译环境搭建好以后,进行图形界面的设计,再到相关网站上下载一些合适的图片素材,利用贴图技术模拟。根据人的坐标变换,将每次循环的过程中都利用一遍贴图技术,这样根据人类视觉的延迟性,就会让我们觉得像放动画一样,将DFS寻路算法的过程清晰的展现在图形界面上。同时找到终点时,控制台也可以打印出相应的路径。


主函数代码设计

​ 在得到上面的详细解题思路后我们可以采用自顶向下的编程思路,先对主函数进行编写:

int main() {

	int count = 1;//用于记录路径条数

	//1.创建地图
	int map[ROWS][COLS] = {
		{1,1,1,1,1,1,1,1},
		{1,0,0,0,0,0,0,1},
		{1,0,1,0,1,0,1,1},
		{1,0,1,0,1,0,1,1},
		{1,0,1,1,1,0,1,1},
		{1,0,0,0,1,0,1,1},
		{1,0,0,0,0,0,0,1},
		{1,1,1,1,1,1,1,1}
	};

	//2.设置遍历数组,用于标记某点是否已经被走过
	bool isWalk[ROWS][COLS] = { false };

	//3.设置起点和终点
	Point startPos = { 1,1,0 };
	Point endPos = { 6,6,0 };

	//3.建立一个栈,将起点压入栈中,并标记起点已走过
	stack<Point> St;
	St.push(startPos);
	isWalk[startPos.x][startPos.y] = true;
	Point searchPos;
	bool isFindPath;//是否找到可走的方向

	Init(map, St);//初始化图形界面


	//4.DFS
	while (!St.empty()){
		Sleep(100);
		putImg(St.top(), map);
		searchPos = St.top();
		//判断是否到达终点
		if (searchPos.x == endPos.y && searchPos.y == endPos.y) {
			stack<Point> path = stackCopy(St);
			cout << "第" << count << "条路径:" << endl;

			//控制台打印路径
			printPath(path);

			//图形界面打印路径
			putRoadImg(St);

			count++;

			//进行回溯
			Backtrack(searchPos, isWalk, St);
		}

		//调用寻路函数
		searchPath(searchPos, isFindPath, St, map, isWalk);

		if (isFindPath) {//找到路了
			GO(searchPos, isWalk, St);//前进
		}
		else {
			Back(isWalk, St);//后退
		}
	}

	//5.关闭图形库总结遍历道路
	closegraph();
	if(count-1 == 0)
		cout << "没有通路" << endl;
	else
		cout << "共" << count - 1 << "条道路,遍历结束" << endl;

	return 0;
}

​ 解题步骤就主要是以上的注释所呈现的,接下来我们将分模块介绍,每个函数模块或类。

(1)Point 类

struct Point//定义一个每个点的结构体
{
	int x;
	int y;
	int dir = 1;//1.下 2.右 3.上 4.左
};

(2) Init函数

​ 该函数主要用于初始化控制台界面和图形界面,将地图以图形界面的形式展现在屏幕上,并实行控制台交互,程序根据用户输入的指令判断是进行寻路还是退出程序。

//图形界面初始化
void Init(int map[ROWS][COLS], stack<Point> St) {
	initgraph(ROWS * SPACE, COLS * SPACE, SHOWCONSOLE);//图形框初始化
	loadImg();//加载图片
	putImg(St.top(), map);
	cout << "地图初始化完成" << endl;
	cout << "是否开始寻路?是请输入1,否请输入0:";
	int choice;
	cin >> choice;
	while (choice != 1 && choice != 0) {
		cout << "请重新输入:";
		cin >> choice;
	}
	if (choice == 1) {
		system("cls");
		cout << "道路遍历开始!!!" << endl;
		return;
	}
	else {
		system("cls");
		cout << "已退出程序" << endl;
		exit(0);
	}
}

(3)putImg函数

​ 用于在指定的位置贴上指定的图片。

​ 动画模拟:因为人的视觉有延迟功能,位置都在每次循环而改变,所以每次循环贴图就可以模拟出一种动画的效果。

//贴图函数
void putImg(Point pos, int map[ROWS][COLS]) {
	for (int i = 0; i < ROWS; i++) {
		for (int j = 0; j < COLS; j++) {
			if (pos.x == i && pos.y == j) {
				putimage(j * SPACE, i * SPACE, &people);
			}
			else if (map[i][j] == 0) {
				putimage(j * SPACE, i * SPACE, &road);
			}
			else {
				putimage(j * SPACE, i * SPACE, &wall);
			}
		}
	}
}

(4)printPath函数

​ 用于输出到达终点后,对栈中的路径进行在控制台的输出。

void printPath(stack<Point> Path) {//用于输出路径
	while (!Path.empty())
	{
		cout << "(" << Path.top().x << "," << Path.top().y << "," << Path.top().dir << ")" << "->";
		Path.pop();
	}
	cout << "\b\b  " << endl;
}

(5) putRoadImg函数

​ 在找到终点后在图形界面上打印足迹。

//路径贴图函数
void putRoadImg(stack<Point> St) {
	stack<Point> temp = St;
	while (!temp.empty()) {
		putimage(temp.top().y * SPACE + SPACE / 4, temp.top().x * SPACE + SPACE / 4, &pos);
		temp.pop();
	}
	Sleep(6000);
}

(6)Backtrack函数

​ 到达终点后,坐标点进行回溯,寻找其他路径。

//回溯函数
void Backtrack(Point& searchPos, bool isWalk[ROWS][COLS], stack<Point>& St) {
	isWalk[St.top().x][St.top().y] = false;
	St.pop();
	searchPos = St.top();
}

(7) searchPath函数

​ 上下左右寻找通路。

//寻路函数
void searchPath(Point& searchPos, bool& isFindPath,stack<Point> &St,int map[ROWS][COLS],bool isWalk[ROWS][COLS]) {
	isFindPath = false;
	while (searchPos.dir <= 4 && isFindPath == false) {
		searchPos.dir++;
		//试探每一个方向
		switch (searchPos.dir) {
		case 1://下
			searchPos.x = St.top().x + 1;
			searchPos.y = St.top().y;
			break;
		case 2://右
			searchPos.x = St.top().x;
			searchPos.y = St.top().y + 1;
			break;
		case 3://上
			searchPos.x = St.top().x - 1;
			searchPos.y = St.top().y;
			break;
		case 4://左
			searchPos.x = St.top().x;
			searchPos.y = St.top().y - 1;
			break;
		}
		if (map[searchPos.x][searchPos.y] == 0 && isWalk[searchPos.x][searchPos.y] == false) {
			isFindPath = true;
		}
	}
}

(8)GO函数

​ 若找到了可走的路,就让角色前进。

//前进函数
void GO(Point & searchPos, bool isWalk[ROWS][COLS], stack<Point>&St) {
	St.top().dir = searchPos.dir;
	St.push(searchPos);
	St.top().dir = 0;
	isWalk[searchPos.x][searchPos.y] = true;
}

9.Back函数

​ 找不到可以走的路了,就退栈,倒退到上一个结点。

//后退函数
void Back(bool isWalk[ROWS][COLS], stack<Point>& St) {
	isWalk[St.top().x][St.top().y] = false;//当前路径标记为未走过
	St.pop();
}



运行结果


在这里插入图片描述

​ 不断寻找路径,不断在控制台上打印。



好!!!一个自动走迷宫,模拟DFS深度寻路算法的程序就这样做完了,想要源码和素材,记得点赞+关注并私信基诺领取哦。关注基诺,你会有更多意想不到的惊喜哦。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值