DFS BFS相关流程与代码

本文以一个走迷宫为例子,其中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 <stdio.h>

#define MAX_ROW  5
#define MAX_COL   5

struct 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<N;i++){
                for(j=0;j<N;j++) {
                    maze_ret[i][j] = maze[i][j];
                }
            }
        }
    }
    

    if ((y < N-1) && (maze[x][y+1]==0 || maze[x][y+1]==2)) traverse(x, y+1, count);
    if ((x < N-1)&& (maze[x+1][y]==0 ||maze[x+1][y]==2)) traverse(x+1, y, count);
    if ((y >= 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 <stdio.h>

#define MAX_ROW  5
#define MAX_COL   5

struct 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的方法来实现查找捡宝石最多的路径,该如何做?

难点:广度优先搜索采用队列的形式,如何回溯?遗憾本人不会,也不知能否这样实现。

posted on 2015-02-15 17:56  木星来客  阅读(486)  评论(1编辑  收藏  举报

导航