迷宮問題 - 隊列與廣度優先搜索


隊列也是一組元素的集合,也提供兩種基本操作:Enqueue(入隊)將元素添加到隊尾,Dequeue(出隊)從隊頭取出元素並返回。就像排隊買票一樣,先來先服務,先入隊的人也是先出隊的,這種方式稱為FIFO(First In First Out,先進先出),有時候隊列本身也被稱為FIFO。

下面我們用隊列解決迷宮問題。程序如下:(參考《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
 
/*************************************************************************
> File Name: breadth_search.c
> Author: Simba
> Mail: dameng34@163.com
> Created Time: 2012年12月24日 星期一 19時24分37秒
************************************************************************/

#include<stdio.h>


#define MAX_ROW 5
#define MAX_COL 5

struct point
{
    int row, col, predecessor;
} queue[512];

int head = 0, 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_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");
}

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    /* 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);
        if (p.row + 1 < MAX_ROW /* down */
                && maze[p.row + 1][p.col] == 0)
            visit(p.row + 1, p.col);
        if (p.col - 1 >= 0  /* left */
                && maze[p.row][p.col - 1] == 0)
            visit(p.row, p.col - 1);
        if (p.row - 1 >= 0  /* up */
                && maze[p.row - 1][p.col] == 0)
            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;
}

輸出為:


其實仍然可以像《用深度優先搜索解迷宮問題》一樣用predecessor數組表示每個點的前趨,但我們可以換一種更方便的數據結構,直接在每個點的結構體中加一個成員表示前趨:
struct point { int row, col, predecessor; } queue[512];

int head = 0, tail = 0;
變量head和tail是隊頭和隊尾指針,head總是指向隊頭,tail總是指向隊尾的下一個元素。每個點的predecessor成員也是一個指針,指向它的前趨在queue數組中的位置。如下圖所示:


這是一個靜態分配的數組,每個數組元素都有row、col和predecessor三個成員,predecessor成員保存一個數組下標,指向數組中的另一個元素,這其實也是鏈表的一種形式,稱為《靜態鏈表》。

為了幫助理解,把這個算法改寫成偽代碼如下圖:


從打印的搜索過程可以看出,這個算法的特點是沿各個方向同時展開搜索,每個可以走通的方向輪流往前走一步,這稱為廣度優先搜索(BFS,Breadth First Search)。探索迷宮和隊列變化的過程如下圖所示。


廣度優先是一種步步為營的策略,每次都從各個方向探索一步,將前線推進一步,圖中的虛線就表示這個前線,隊列中的元素總是由前線的點組成的,可見正是隊列先進先出的性質使這個算法具有了廣度優先的特點。廣度優先搜索還有一個特點是可以找到從起點到終點的最短路徑,而深度優先搜索找到的不一定是最短路徑。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM