深度优先搜索找迷宫的出路
3. 深度优先搜索
现在我们用堆栈解决一个有意思的问题,定义一个二维数组:
int maze[5][5] = {0, 1, 0, 0, 0,0, 1, 0, 1, 0,0, 0, 0, 0, 0,0, 1, 1, 1, 0,0, 0, 0, 1, 0, };
它表示一个迷宫,其中的1表示墙壁,0表示可以走的路,只能横着走或竖着走,不能斜着走,要求编程序找出从左上角到右下角的路线。程序如下:
例 12.3. 用深度优先搜索解迷宫问题
#include <stdio.h>#define MAX_ROW 5 #define MAX_COL 5struct point { int row, col; } stack[512]; int top = 0;void push(struct point p) {stack[top++] = p; }struct point pop(void) {return stack[--top]; }int is_empty(void) {return top == 0; }int maze[MAX_ROW][MAX_COL] = {0, 1, 0, 0, 0,0, 1, 0, 1, 0,0, 0, 0, 0, 0,0, 1, 1, 1, 0,0, 0, 0, 1, 0, };void print_maze(void) {int i, j;for (i = 0; i < MAX_ROW; i++) {for (j = 0; j < MAX_COL; j++)printf("%d ", maze[i][j]);putchar('\n');}printf("*********\n"); }struct point predecessor[MAX_ROW][MAX_COL] = {{{-1,-1}, {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1}},{{-1,-1}, {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1}},{{-1,-1}, {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1}},{{-1,-1}, {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1}},{{-1,-1}, {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1}}, };void visit(int row, int col, struct point pre) {struct point visit_point = { row, col };maze[row][col] = 2;predecessor[row][col] = pre;push(visit_point); }int main(void) {struct point p = { 0, 0 };maze[p.row][p.col] = 2;push(p); while (!is_empty()) {p = pop();if (p.row == MAX_ROW - 1 /* goal */&& p.col == MAX_COL - 1)break;if (p.col+1 < MAX_COL /* right */&& maze[p.row][p.col+1] == 0)visit(p.row, p.col+1, p);if (p.row+1 < MAX_ROW /* down */&& maze[p.row+1][p.col] == 0)visit(p.row+1, p.col, p);if (p.col-1 >= 0 /* left */&& maze[p.row][p.col-1] == 0)visit(p.row, p.col-1, p);if (p.row-1 >= 0 /* up */&& maze[p.row-1][p.col] == 0)visit(p.row-1, p.col, p);print_maze();}if (p.row == MAX_ROW - 1 && p.col == MAX_COL - 1) {printf("(%d, %d)\n", p.row, p.col);while (predecessor[p.row][p.col].row != -1) {p = predecessor[p.row][p.col];printf("(%d, %d)\n", p.row, p.col);}} elseprintf("No path!\n");return 0; }
运行结果如下:
2 1 0 0 0 2 1 0 1 0 0 0 0 0 0 0 1 1 1 0 0 0 0 1 0 ********* 2 1 0 0 0 2 1 0 1 0 2 0 0 0 0 0 1 1 1 0 0 0 0 1 0 ********* 2 1 0 0 0 2 1 0 1 0 2 2 0 0 0 2 1 1 1 0 0 0 0 1 0 ********* 2 1 0 0 0 2 1 0 1 0 2 2 0 0 0 2 1 1 1 0 2 0 0 1 0 ********* 2 1 0 0 0 2 1 0 1 0 2 2 0 0 0 2 1 1 1 0 2 2 0 1 0 ********* 2 1 0 0 0 2 1 0 1 0 2 2 0 0 0 2 1 1 1 0 2 2 2 1 0 ********* 2 1 0 0 0 2 1 0 1 0 2 2 0 0 0 2 1 1 1 0 2 2 2 1 0 ********* 2 1 0 0 0 2 1 0 1 0 2 2 2 0 0 2 1 1 1 0 2 2 2 1 0 ********* 2 1 0 0 0 2 1 2 1 0 2 2 2 2 0 2 1 1 1 0 2 2 2 1 0 ********* 2 1 2 0 0 2 1 2 1 0 2 2 2 2 0 2 1 1 1 0 2 2 2 1 0 ********* 2 1 2 2 0 2 1 2 1 0 2 2 2 2 0 2 1 1 1 0 2 2 2 1 0 ********* 2 1 2 2 2 2 1 2 1 0 2 2 2 2 0 2 1 1 1 0 2 2 2 1 0 ********* 2 1 2 2 2 2 1 2 1 2 2 2 2 2 0 2 1 1 1 0 2 2 2 1 0 ********* 2 1 2 2 2 2 1 2 1 2 2 2 2 2 2 2 1 1 1 0 2 2 2 1 0 ********* 2 1 2 2 2 2 1 2 1 2 2 2 2 2 2 2 1 1 1 2 2 2 2 1 0 ********* 2 1 2 2 2 2 1 2 1 2 2 2 2 2 2 2 1 1 1 2 2 2 2 1 2 ********* (4, 4) (3, 4) (2, 4) (1, 4) (0, 4) (0, 3) (0, 2) (1, 2) (2, 2) (2, 1) (2, 0) (1, 0) (0, 0)
这次堆栈里的元素是结构体类型的,用来表示迷宫中一个点的x和y坐标。我们用一个新的数据结构保存走迷宫的路线,每个走过的点都有一个前趋(Predecessor)点,表示是从哪儿走到当前点的,比如predecessor[4][4]
是坐标为(3, 4)的点,就表示从(3, 4)走到了(4, 4),一开始predecessor
的各元素初始化为无效坐标(-1, -1)。在迷宫中探索路线的同时就把路线保存在predecessor
数组中,已经走过的点在maze
数组中记为2防止重复走,最后找到终点时就根据predecessor
数组保存的路线从终点打印到起点。为了帮助理解,我把这个算法改写成伪代码(Pseudocode)如下:
将起点标记为已走过并压栈; while (栈非空) {从栈顶弹出一个点p;if (p这个点是终点)break;否则沿右、下、左、上四个方向探索相邻的点if (和p相邻的点有路可走,并且还没走过)将相邻的点标记为已走过并压栈,它的前趋就是p点; } if (p点是终点) {打印p点的坐标;while (p点有前趋) {p点 = p点的前趋;打印p点的坐标;} } else没有路线可以到达终点;
我在while
循环的末尾插了打印语句,每探索一步都打印出当前迷宫的状态(标记了哪些点),从打印结果可以看出这种搜索算法的特点是:每次探索完各个方向相邻的点之后,取其中一个相邻的点走下去,一直走到无路可走了再退回来,取另一个相邻的点再走下去。这称为深度优先搜索(DFS,Depth First Search)。探索迷宫和堆栈变化的过程如下图所示。
图 12.2. 深度优先搜索
图中各点的编号表示探索顺序,堆栈中保存的应该是坐标,我在画图时为了直观就把各点的编号写在堆栈里了。可见正是堆栈后进先出的性质使这个算法具有了深度优先的特点。如果在探索问题的解时走进了死胡同,则需要退回来从另一条路继续探索,这种思想称为回溯(Backtrack),一个典型的例子是很多编程书上都会讲的八皇后问题。
最后我们打印终点的坐标并通过predecessor
数据结构找到它的前趋,这样顺藤摸瓜一直打印到起点。那么能不能从起点到终点正向打印路线呢?在上一节我们看到,数组支持随机访问也支持顺序访问,如果在一个循环里打印数组,既可以正向打印也可以反向打印。但predecessor
这种数据结构却有很多限制:
不能随机访问一条路线上的任意点,只能通过一个点找到另一个点,通过另一个点再找第三个点,因此只能顺序访问。
每个点只知道它的前趋是谁,而不知道它的后继(Successor)是谁,所以只能反向顺序访问。
可见,有什么样的数据结构就决定了可以用什么样的算法。那为什么不再建一个successor
数组来保存每个点的后继呢?从DFS算法的过程可以看出,虽然每个点的前趋只有一个,后继却不止一个,如果我们为每个点只保存一个后继,则无法保证这个后继指向正确的路线。由此可见,有什么样的算法就决定了可以用什么样的数据结构。设计算法和设计数据结构这两件工作是紧密联系的。
深度优先搜索找迷宫的出路相关推荐
- Qt利用深度优先搜索实现迷宫寻宝
先看效果: 利用QT搭建迷宫界面 利用单选框来构造迷宫,设置障碍物和宝藏的位置 利用深度优先搜索实现迷宫寻宝,并且在寻找的过程中避开障碍物. 搜索算法在线程中进行 定义方格的属性 enum Signa ...
- 打印数组算法:堆栈与深度优先搜索(迷宫问题)
每日一贴,今天的内容关键字为打印数组 栈堆的拜访规矩被制约为Push和Pop两种作操,Push(入栈或压栈)向栈顶添加元素,Pop(出栈或弹出)则掏出前当栈顶的元素,也就是说,只能拜访栈顶元素而不能拜 ...
- 深度优先搜索—走迷宫
例题:走迷宫 1.需要一个终止条件, 2.需要四个搜索方向:上下左右: 3.并且需要使用bool进行标记一个方向是否访问过: 4.如果需要找到多个可行解,那么如果该方向可行,需要取消标记.如果只需要也 ...
- 基于A*搜索和深度优先搜索解迷宫问题
摘 要 迷宫问题是指能够从起始点寻找一条通往目标点的路径,迷宫的传统搜索是采用深度优先和宽度优先搜索,虽然也能够解决迷宫的求解问题,但是这些方法效率比较低.我们已经知道深度优先和广度优先搜索归于为盲目 ...
- 有向图寻找环路python_有向图的深度优先搜索 —— 找出环路
一.目标 找出下图(有向图)中的回路 二.策略 1,深度优先搜索:顾名思义,就是从某个顶点开始探索,会一直探索到某个可能路径的尽头才会停止探索,并原路返回(下面的例子, 并不会一直原路返回到原点,而是 ...
- 用深度优先搜索解迷宫问题
定义一个二维数组: int maze[5][5] = {0, 1, 0, 0, 0,0, 1, 0, 1, 0,0, 0, 0, 0, 0,0, 1, 1, 1, 0,0, 0, 0, 1, 0, } ...
- 深度优先搜索——走迷宫问题
题目大意: 用二维数组存储一个n * m的迷宫,0表示路,1表示墙.一个人位于左上角 (1,1)(1,1) 处,已知该人每次可以向上.下.左.右任意一个方向移动一个位置,求从左上角移动到右下角的最少次 ...
- DFS(深度优先搜索)---迷宫(洛谷)----SCAU_LEO
一.题目 题目链接:迷宫 首先,今天是1月2日,虽然有点晚了,但是还是祝大家新的一年快快乐乐,开开心心,题题AC,场场拿金,! 说说我对迷宫类题目的感受,我记得刚开始学迷宫的时候,不是看的很懂,但是睡 ...
- 深度优先搜索之迷宫寻宝
迷宫寻宝 有一个N行M列由单元格组成的迷宫.迷宫中埋藏有宝物,每一个单元格可能为空地,为障碍物,为宝藏.现在请你写一个程序求从起点到宝藏的最短步数. 输入格式:第一行输入N M表示地图的行和列.后面N ...
最新文章
- 码农与程序员的惊人差别
- html5相关笔记(一)
- ubuntu19.10安装codeblocks20.03
- jQuery plugin 开发的一个例子
- css比较特殊选择器汇总(持续更新)
- Optimal Step Nonrigid ICP Algorithms for surface registration
- linux link path walk,python之os.walk()与os.path.walk()
- 反向传播与梯度消失梯度爆炸
- 头目说“我不参与研发和测试的工作”,包含哪几个意思
- 北京市丰台区卫星地图下载
- 愤怒的导数:一点可导和邻域内可导能推出来什么?
- 软件需求工程2018期末题
- Linux 最最最常用的命令
- cannot open file /storage/emulated/0/DCIM/Camera/yinpin.mp4 (Permission denied)
- 上位机开发——数据库系列问题一网打尽
- 嵌入式软件测试之初体验
- 搜索导航HTML,CSS 带搜索导航栏的示例代码
- 配置SCOOP,及使用国内源
- 外设篇:SD卡等外存设备
- NNDL 实验三 线性模型
热门文章
- 学习虚幻4需要储备的知识(2D游戏开发者向)
- 时隔这么长时间,又回来写博客了
- (NO.00003)iOS游戏简单的机器人投射游戏成形记(十二)
- Zabbix-2.2.4,显示中文语言的选项方法
- [每日一题] OCP1z0-047 :2013-08-17 EXTERNAL TABLE――加载数据 ............................56...
- 关于yum的/repodata/repomd.xml错误
- SQL Server 将指定的数据库中的所有表都列出来
- 文件上传(上传至独立的文件服务器)
- 防范SQL注入的几种方法 4
- ymPrompt.js消息提示组件