三、【圖演算法】廣度優先搜尋(BFS)
阿新 • • 發佈:2018-12-08
圖演算法是個龐大的家族,其中大部分成員的主體框架都可以歸結於圖的遍歷。圖的遍歷需要訪問所有頂點一次且僅 一次,此外,圖遍歷同時還要訪問所有的邊一次且僅一次。經過一次遍歷,樹邊的頂點共同構成了原圖的一顆遍歷樹。
為防止對頂點的重複訪問,在遍歷的過程中,需要動態地設定各頂點的不同狀態,並且隨著遍歷的程序不斷地轉換狀態,直至最後的“訪問完畢”,圖的遍歷更加強調對處於特定狀態頂頂啊的甄別和查詢,故也稱作圖搜尋。
最基本而典型的圖搜尋演算法包括:廣度優先搜尋(BFS),深度優先搜尋(DFS),優先順序搜尋等(PFS),本文主要介紹圖的廣度優先搜尋(breadth-first search,BFS)
策略:越早被訪問到的頂點,其鄰居頂點越優先被選用。始自圖中頂點s的BFS搜尋,將首先訪問頂點s,再依次訪問頂點s所有未成訪問過的鄰居頂點,再按照後者被訪問的先後次序,組個訪問它們的鄰居。這個過程很類似與樹的層次遍歷,具有先入先出的特點,故採用佇列結構作為輔助容器。
實現:由於整個圖可能具有多個連通域,從單個頂點開始的BFS可能不能遍歷到圖中的所有頂點,所以BFS函式能夠遍歷從頂點s開始的單個連通域,而bfs函式則對所有頂點進行檢查,只要未曾被訪問過,就從該點開始一次新的BFS搜尋,這樣就能保證所有的連通域都能夠被遍歷到。
template<typename Tv, typename Te> void graph<Tv, Te>::BFS(int v, int& clock) //遍歷單個 { queue<int> Q; //頂點快取佇列 status(v) = DISCOVERED; //標記頂點為已發現 Q.enqueue(v); //將當前頂點入隊 while (!Q.empty()) //只要佇列非空,則繼續 { v = Q.dequeue(); //每次選擇一個頂點出隊,遍歷其所有鄰居,檢查是否存在關聯邊 dTime(v) = ++clock; for (int u = firstNbr(v); u >= 0; u = nextNbr(v, u)) //對於頂點v,從頂點集V的最後一個元素開始尋找鄰居(與v存在關聯邊) { if (status(u) == UNDISCOVERED) //如果此鄰居頂點尚未發現 { cout << "(v,u)" << "(" << v<<"," << u << ")" << endl; status(u) = DISCOVERED; //設定該頂點為已被發現 type(v, u) = TREE; //設定邊e(v,u)為TREE(遍歷樹) parent(u) = v; //設定在遍歷樹中頂點u的父親為v Q.enqueue(u); //頂點u入隊 } else //如果此鄰居頂點已經被發現 { type(v, u) = CROSS; //設定邊e(v,u)為CROSS(跨邊),不是遍歷樹枝幹 } } status(v) = VISITED; //設定頂點v為已遍歷 } } template<typename Tv, typename Te> void graph<Tv, Te>::bfs(int s) { reset(); //復位所有頂點和已存在邊的狀態為未被發現,未確定 int clock = 0; //時間標籤 int v = s; do { if(status(v)==UNDISCOVERED) BFS(v,clock); //對每個頂點都進行一次單連通域廣度優先搜尋 v++; cout << "v----" << v << endl; } while ((v = (++v%n)) != s); }
效率:若圖G=(V,E)中共有n個頂點和e條邊,則BFS僅需O(n+e)時間。