本文以一个走迷宫为例子,其中1表示墙壁,0表示可以走的路,只能上下左右走,不能斜着走,要求找到左上角到右下角的线路。
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,如上图所示是一个5X5的迷宫。
dfs可以使用堆栈实现,也可以使用递归的方式实现。利用堆栈的形式伪代码如下:
DFS:
将起点标记为已走过的点,并压栈;
while(栈不为空)
{
从栈顶弹出一个点P;
if(p是终点)
break;
否则沿上下左右四个方向探索
if(和P相邻的点有路可走,并且没有走过)
将相邻的点标记为已走过点,并且将改点压栈,改点的前驱就是P点
}
if (P是终点)
{
打印P店的坐标;
while(p有前驱)
{
P = P 的前驱;
打印P坐标;
}
else
没有路线走出迷宫
BFS则是使用队列的方式实现,能够找到最短的路径
将起点标记为已走过的点,并入队列;
while(队列不为空)
{
出队列一个点P;
if(p是终点)
break;
否则沿上下左右四个方向探索
if(和P相邻的点有路可走,并且没有走过)
将相邻的点标记为已走过点,并且将改点入队列,该点的前驱就是P点
}
if (P是终点)
{
打印P店的坐标;
while(p有前驱)
{
P = P 的前驱;
打印P坐标;
}
else
没有路线走出迷宫
#include#define MAX_ROW 5#define MAX_COL 5struct point{ int row; int 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_ROW] = { 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]); printf("\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 && p.col == MAX_COL - 1) break; if(p.col + 1 < MAX_COL && 0 == maze[p.row][p.col+1]) visit(p.row, p.col+1, p); if(p.row + 1 < MAX_ROW && 0 == maze[p.row+1][p.col]) visit(p.row+1, p.col, p); if(p.col - 1 >= 0 && 0 == maze[p.row][p.col-1]) visit(p.row, p.col-1, p); if(p.row - 1 >= 0 && 0 == maze[p.row-1][p.col]) 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;}
上述代码是使用堆栈的方法实现DFS走迷宫的,入口是左上角,出口右下角。其中打印走迷宫的步骤是逆向打印。
值得注意的是,如果其中找到的终点不break;则可以找到所有的走出迷宫的走路路径。
那么问题来了,能输出所有的路径,那么有什么意义呢?
请看下面的一道题:走迷宫捡宝石,走哪条路捡到的最多。
问题描述如下:
一个迷宫里,0代表路,1代表墙,2代表地上有宝石。不能走重复的路线,如何走出去并同时捡到最多的宝石?
核心部分代码,这次采用的递归的方式实现,道理和堆栈式一样的。
主函数中2句核心代码就是:
jewels_count = 0;traverse(0, 0,0);
jewels_count = 0;traverse(0, 0,0);void traverse(int x, int y, int count){ int i, j, nTemp; if (maze[x][y]==2) { count++; } nTemp = maze[x][y]; maze[x][y]=3; if (x==N-1 && y==N-1){ if (count>jewels_count){ jewels_count=count; for(i=0;i= 1)&& (maze[x][y-1]==0 || maze[x][y-1]==2)) traverse(x, y-1, count); if ((x >= 1)&& (maze[x-1][y]==0 || maze[x-1][y]==2)) traverse(x-1, y, count); maze[x][y]=nTemp;}
这个代码的关键部分,就是最后一句 maze[x][y] = nTemp; 用于回溯。 这部分代码使用堆栈本人没能写出来。
下面的是BFS的代码,同样,打印走的路径也是逆向打印的。
#include#define MAX_ROW 5#define MAX_COL 5struct point{ int row; int col; int predecessor;}queue[512];int head = 0;int tail = 0;void enqueue(struct point p){ queue[tail++] = p;}struct point dequeue(void){ return queue[head++];}int is_empty(void){ return head == tail;}int maze[MAX_ROW][MAX_ROW] = { 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]); printf("\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 visit_point = {row, col, head-1}; maze[row][col] = 2; enqueue(visit_point);}int main(void){ struct point p = { 0, 0, -1}; maze[p.row][p.col] = 2; enqueue(p); while( ! is_empty() ) { p = dequeue(); if (p.row == MAX_ROW - 1 && p.col == MAX_COL - 1) break; if(p.col + 1 < MAX_COL && 0 == maze[p.row][p.col+1]) visit(p.row, p.col+1); if(p.row + 1 < MAX_ROW && 0 == maze[p.row+1][p.col]) visit(p.row+1, p.col); if(p.col - 1 >= 0 && 0 == maze[p.row][p.col-1]) visit(p.row, p.col-1); if(p.row - 1 >= 0 && 0 == maze[p.row-1][p.col]) visit(p.row-1, p.col); print_maze(); } if (p.row == MAX_ROW - 1 && p.col == MAX_COL - 1) { printf("(%d,%d)\n", p.row, p.col); while(p.predecessor != -1) { p = queue[p.predecessor]; printf("(%d,%d)\n", p.row, p.col); } } else { printf("No path!\n"); } return 0;}
如果我们采用BFS的方法来实现查找捡宝石最多的路径,该如何做?
难点:广度优先搜索采用队列的形式,如何回溯?遗憾本人不会,也不知能否这样实现。