三、【圖演算法】深度優先搜尋(DFS)
阿新 • • 發佈:2018-12-08
圖演算法是個龐大的家族,其中大部分成員的主體框架都可以歸結於圖的遍歷。圖的遍歷需要訪問所有頂點一次且僅 一次,此外,圖遍歷同時還要訪問所有的邊一次且僅一次。經過一次遍歷,樹邊的頂點共同構成了原圖的一顆遍歷樹。
為防止對頂點的重複訪問,在遍歷的過程中,需要動態地設定各頂點的不同狀態,並且隨著遍歷的程序不斷地轉換狀態,直至最後的“訪問完畢”,圖的遍歷更加強調對處於特定狀態頂頂啊的甄別和查詢,故也稱作圖搜尋。
最基本而典型的圖搜尋演算法包括:廣度優先搜尋(BFS),深度優先搜尋(DFS),優先順序搜尋等(PFS),本文主要介紹圖的深度優先搜尋(depth-first search,DFS),本文使用的圖資料結構參見之前部落格 https://blog.csdn.net/qq_18108083/article/details/84870399
策略:優先選取最後一個被訪問到的頂點的鄰居。始自圖中頂點s的BFS搜尋,將首先訪問頂點s,再從s所有尚未訪問到的鄰居中任取一個頂點,並以此為基點,遞迴地執行DFS搜尋。這個過程具有先入先後的特點,故採用棧結構作為輔助容器或直接使用遞迴方法,這裡使用遞迴進行實現。
實現:由於整個圖可能具有多個連通域,從單個頂點開始的DFS可能不能遍歷到圖中的所有頂點,所以DFS函式能夠遍歷從頂點s開始的單個連通域,而dfs函式則對所有頂點進行檢查,只要未曾被訪問過,就從該點開始一次新的DFS搜尋,這樣就能保證所有的連通域都能夠被遍歷到。
template<typename Tv, typename Te> void graph<Tv, Te>::DFS(int v, int& clock) { status(v) = DISCOVERED; //標記當前節點為發現 dTime(v) = ++clock; for (int u = firstNbr(v); u > -1; u = nextNbr(v, u)) //遍歷所有鄰居頂點 { switch (status(u)) { case UNDISCOVERED: //尚未發現的頂點,繼續深入遍歷 status(u) = DISCOVERED; //標記為已發現 type(v, u) = TREE; parent(u) = v; DFS(u, clock); break; case DISCOVERED: //已被發現但是尚未遍歷完成的頂點,那就是祖先啊 type(v, u) = BACKWARD; break; default: //VISITED 已經遍歷完成,根據dTime判斷是FORWARD還是CROSS type(v, u) = (dTime(v) < dTime(u)) ? FORWARD : CROSS; break; } } status(v) = VISITED; fTime(v) = ++clock; } template<typename Tv, typename Te> void graph<Tv, Te>::dfs(int s) { reset(); //復位所有頂點和已存在邊的狀態為未被發現,未確定 int clock = 0; //時間標籤 int v = s; do { if (status(v) == UNDISCOVERED) DFS(v, clock); //對每個頂點都進行一次單連通域深度優先搜尋 cout << "v----" << v << endl; } while ((v=(++v%n)) != s); }
效率:若圖G=(V,E)中共有n個頂點和e條邊,則DFS僅需O(n+e)時間。