迷宫问题 - 堆栈与深度优先搜索

堆栈的访问规则被限制为Push和Pop两种操作,Push(入栈或压栈)向栈顶添加元素,Pop(出栈或弹出)则取出当前栈顶的元素,也就是说,只能访问栈顶元素而不能访问栈中其它元素。

现在我们用堆栈解决一个有意思的问题,定义一个二维数组:

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表示可以走的路,只能横着走或竖着走,不能斜着走,要求编程序找出从左上角到右下角的路线。程序如下:(参考《Linux c 编程一站式学习》)

C++ Code


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

 

#include<stdio.h>

typedef struct point

{

int row, col;

} item_t;

#define MAX_ROW 5

#define MAX_COL 5

static item_t stack[512];

static int top = 0;

void push(item_t p)

{

stack[top++] = p;

}

item_t 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);

}

}

else

printf("No path!\n");

return 0;

}

输出为:

这次堆栈里的元素是结构体类型的,用来表示迷宫中一个点的x和y坐标。我们用一个新的数据结构保存走迷宫的路线,每个走过的点都有一个前趋(Predecessor)点,表示是从哪儿走到当前点的,比如predecessor[4][4]是坐标为(3, 4)的点,就表示从(3, 4)走到了(4, 4),一开始predecessor的各元素初始化为无效坐标(-1,

-1)。在迷宫中探索路线的同时就把路线保存在predecessor数组中,已经走过的点在maze数组中记为2防止重复走,最后找到终点时就根据predecessor数组保存的路线从终点打印到起点。为了帮助理解,把这个算法改写成伪代码(Pseudocode)如下图:

程序在while循环的末尾插了打印语句,每探索一步都打印出当前迷宫的状态(标记了哪些点),从打印结果可以看出这种搜索算法的特点是:每次探索完各个方向相邻的点之后,取其中一个相邻的点走下去,一直走到无路可走了再退回来,取另一个相邻的点再走下去。这称为深度优先搜索(DFS,Depth First Search)。探索迷宫和堆栈变化的过程如下图所示。

图中各点的编号表示探索顺序,堆栈中保存的应该是坐标,在画图时为了直观就把各点的编号写在堆栈里了。可见正是堆栈后进先出的性质使这个算法具有了深度优先的特点。如果在探索问题的解时走进了死胡同,则需要退回来从另一条路继续探索,这种思想称为回溯(Backtrack),一个典型的例子是很多编程书上都会讲的八皇后问题。

最后我们打印终点的坐标并通过predecessor数据结构找到它的前趋,这样顺藤摸瓜一直打印到起点。那么能不能从起点到终点正向打印路线呢?,数组支持随机访问也支持顺序访问,如果在一个循环里打印数组,既可以正向打印也可以反向打印。但predecessor这种数据结构却有很多限制:

1. 不能随机访问一条路线上的任意点,只能通过一个点找到另一个点,通过另一个点再找第三个点,因此只能顺序访问。

2. 每个点只知道它的前趋是谁,而不知道它的后继(Successor)是谁,所以只能反向顺序访问。

可见,有什么样的数据结构就决定了可以用什么样的算法。那为什么不再建一个successor数组来保存每个点的后继呢?从DFS算法的过程可以看出,虽然每个点的前趋只有一个,后继却不止一个,如果我们为每个点只保存一个后继,则无法保证这个后继指向正确的路线。由此可见,有什么样的算法就决定了可以用什么样的数据结构。设计算法和设计数据结构这两件工作是紧密联系的。

参考:《Linux c 编程一站式学习》

原文地址:https://www.cnblogs.com/alantu2018/p/8471663.html

时间: 2024-07-31 14:04:24

迷宫问题 - 堆栈与深度优先搜索的相关文章

迷宫寻址中深度优先搜索的递归和非递归算法比较

巧若拙(欢迎转载,但请注明出处:http://blog.csdn.net/qiaoruozhuo) 本文只探究迷宫寻址中深度优先搜索的递归和非递归算法比较,其他相关代码详见<迷宫问题(巧若拙)>http://blog.csdn.net/qiaoruozhuo/article/details/41020745 深度优先搜索的递归算法是很容易实现的,只需设置一个驱动函数,然后递归调用子函数就可以了. 代码如下: int DeepSearchWay()//寻找路径:深度搜索 { CopyMiGong

深度优先搜索[奥数等式、迷宫找人]

上一节我们用暴力枚举解决奥数等式,虽然简单,但是很蛋疼. http://blog.csdn.net/wtyvhreal/article/details/43267867 这一节讲用深搜的方式解决,高大上. 深度优先搜索(Depth First Search,DFS),理解深搜的关键在于解决"当下该如何做".至于"下一步如何做"则与"当下该如何做"是一样的.通常的方法就是把每一种可能都是尝试一遍.当前这一步解决后便进入下一步.下一步的解决方法和当前

unity 使用深度优先搜索生成迷宫之二

之前写过一篇使用深度优先搜索生成随机迷宫的文章 https://www.cnblogs.com/JinT-Hwang/p/9599913.html 今天做了一下优化,使用unity的TileMap来做,并且代码减少到100行以内. 先看一下效果图 下面直接是代码,至于在unity中怎么创建tilemap资源这里就不讲了: using System.Collections; using System.Collections.Generic; using UnityEngine; using Uni

深度优先搜索(DFS)

定义: (维基百科:https://en.wikipedia.org/wiki/Depth-first_search) 深度优先搜索算法(Depth-First-Search),是搜索算法的一种.是沿着树的深度遍历树的节点,尽可能深的搜索树的分支.当节点v的所有边都己被探寻过,搜索将回溯到发现节点v的那条边的起始节点.这一过程一直进行到已发现从源节点可达的所有节点为止.如果还存在未被发现的节点,则选择其中一个作为源节点并重复以上过程,整个进程反复进行直到所有节点都被访问为止(属于盲目搜索). 基

图的遍历之深度优先搜索(DFS)

深度优先搜索(depth-first search)是对先序遍历(preorder traversal)的推广.”深度优先搜索“,顾名思义就是尽可能深的搜索一个图.想象你是身处一个迷宫的入口,迷宫中的路每一个拐点有一盏灯是亮着的,你的任务是将所有灯熄灭,按照DFS的做法如下: 1. 熄灭你当前所在的拐点的灯 2. 任选一条路向前(深处)走,每经过一个拐点将灯熄灭直到与之相邻的拐点的灯全部熄灭后,原路返回到某个拐点的相邻拐点灯是亮着的,走到灯亮的拐点,重复执行步骤1 3. 当所有灯熄灭时,结束 将

leetcode_113题——Path Sum II(深度优先搜索)

Path Sum II Total Accepted: 41402 Total Submissions: 155034My Submissions Question Solution Given a binary tree and a sum, find all root-to-leaf paths where each path's sum equals the given sum. For example:Given the below binary tree and sum = 22, 5

算法7-3:深度优先搜索

深度优先搜索最初是因为迷宫游戏而诞生的.在一个迷宫中,有一个入口和一个出口,其中只有一条路径能从入口到达出口.在走迷宫的时候,每次将走过的地方进行标记,遇到死胡同的时候可以沿着进来的路线后退,找到新的没走过的拐角再尝试新的路线.这种方法的效率很高,因为每个地方只需要走过一次即可.其实,这就是深度优先搜索. 深度优先搜索的目标就是系统化地遍历整个图,让算法的效率更高. 应用 深度优先搜索有几个非常典型的应用: 找出源顶点能到达的所有顶点 找出两个顶点之间的路径 判断两个顶点是否连通 基本思想 深度

&quot;《算法导论》之‘图’&quot;:深度优先搜索、宽度优先搜索及连通分量

本文兼参考自<算法导论>及<算法>. 以前一直不能够理解深度优先搜索和广度优先搜索,总是很怕去碰它们,但经过阅读上边提到的两本书,豁然开朗,马上就能理解得更进一步.  1. 深度优先搜索  1.1 迷宫搜索 在<算法>这本书中,作者写了很好的一个故事.这个故事让我马上理解了深度优先搜索的思想. 如下图1-1所示,如何在这个迷宫中找到出路呢?方法见图1-2. 图1-1 等价的迷宫模型 探索迷宫而不迷路的一种古老办法(至少可以追溯到忒修斯和米诺陶的传说)叫做Tremaux搜

深度优先搜索-linux上浅显易懂的例子

上次看啊哈算法中的深度优先搜索,自己用的是linux(linux粉,windows黑,嘿嘿),字符界面,为了强化对这个的理解,就在linux上对这个例子的代码做了一点修改可以很清楚的看到整个搜索过程,相当于动态的展示吧,虽然不是动画,本来想用QT来写的,不过实在是没时间(其实是QT太久没用了.....) 深度优先搜索说到底就是一条道走到黑直到达到目的,然后再往回走,看看上次选择的地方是否还有别的路可以选择走.代码中定义了宏MAX_NUM=8, 就是8x8的迷宫, 用户输入开始点和结束点,找到开始