深度優先搜尋找迷宮的出路

2021-06-13 10:32:38 字數 4319 閱讀 3352

現在我們用堆疊解決乙個有意思的問題,定義乙個二維陣列:

int maze[5][5] = ;
它表示乙個迷宮,其中的1表示牆壁,0表示可以走的路,只能橫著走或豎著走,不能斜著走,要求程式設計序找出從左上角到右下角的路線。程式如下:

例 12.3. 用深度優先搜尋解迷宮問題

#include #define max_row 5

#define max_col 5

struct point stack[512];

int top = 0;

void push(struct point p)

struct point pop(void)

int is_empty(void)

int maze[max_row][max_col] = ;

void print_maze(void)

printf("*********\n");

}struct point predecessor[max_row][max_col] = , , , , },

, , , , },

, , , , },

, , , , },

, , , , },

};void visit(int row, int col, struct point pre)

; maze[row][col] = 2;

predecessor[row][col] = pre;

push(visit_point);

}int main(void)

; maze[p.row][p.col] = 2;

push(p);

while (!is_empty())

if (p.row == max_row - 1 && p.col == max_col - 1)

} else

printf("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 (棧非空)

if (p點是終點)

} else

沒有路線可以到達終點;

我在while迴圈的末尾插了列印語句,每探索一步都列印出當前迷宮的狀態(標記了哪些點),從列印結果可以看出這種搜尋演算法的特點是:每次探索完各個方向相鄰的點之後,取其中乙個相鄰的點走下去,一直走到無路可走了再退回來,取另乙個相鄰的點再走下去。這稱為深度優先搜尋(dfs,depth first search)。探索迷宮和堆疊變化的過程如下圖所示。

圖 12.2. 深度優先搜尋

圖中各點的編號表示探索順序,堆疊中儲存的應該是座標,我在畫圖時為了直觀就把各點的編號寫在堆疊裡了。可見正是堆疊後進先出的性質使這個演算法具有了深度優先的特點。如果在探索問題的解時走進了死胡同,則需要退回來從另一條路繼續探索,這種思想稱為回溯(backtrack),乙個典型的例子是很多程式設計書上都會講的八皇后問題。

最後我們列印終點的座標並通過predecessor資料結構找到它的前趨,這樣順藤摸瓜一直列印到起點。那麼能不能從起點到終點正向列印路線呢?在上一節我們看到,陣列支援隨機訪問也支援順序訪問,如果在乙個迴圈裡列印陣列,既可以正向列印也可以反向列印。但predecessor這種資料結構卻有很多限制:

不能隨機訪問一條路線上的任意點,只能通過乙個點找到另乙個點,通過另乙個點再找第三個點,因此只能順序訪問。

每個點只知道它的前趨是誰,而不知道它的後繼(successor)是誰,所以只能反向順序訪問。

可見,有什麼樣的資料結構就決定了可以用什麼樣的演算法。那為什麼不再建乙個successor陣列來儲存每個點的後繼呢?從dfs演算法的過程可以看出,雖然每個點的前趨只有乙個,後繼卻不止乙個,如果我們為每個點只儲存乙個後繼,則無法保證這個後繼指向正確的路線。由此可見,有什麼樣的演算法就決定了可以用什麼樣的資料結構。設計演算法和設計資料結構這兩件工作是緊密聯絡的。

深度優先搜尋DFS(迷宮問題)

問題及 給出迷宮的圖紙和初始終點位置,用dfs求最小步數。include using namespace std int n,m,p,q,min 99999999 int a 51 51 book 51 51 void dfs int x,int y,int step 順時針 右下左上 int tx...

深度優先搜尋應用 走迷宮

走迷宮問題是深度優先搜尋的乙個典型應用,通常迷宮的形狀如下 0為可走的道路,1為牆壁。通常情況下一些變種的模型,會加入一些特殊項,有別的意思,比如數字5代表鑰匙,當然複雜模型先不討論,從最簡單的開始。include include include include using namespace st...

走迷宮(深度優先搜尋版)

includeusing namespace std int a 50 50 book 50 50 n,m,p,q 定義全域性變數,二維陣列a用來儲存n行m列的迷宮,book陣列用來標記。p,q為目的地座標,min記錄最小步數 void dfs int x,int y,int step dfs函式用...