前言
查閱了網上許多關於通過DFS算法對有向圖中所有簡單回路的查找,發現有很多關於使用DFS求解有向回路中所有簡單回路的帖子,(在按照節點編號情況下)但大多數僅僅尋找了編號遞增的回路。又或者未對結果去重。P.S.下述有向圖中所有節點均使用數字進行編號,如節點0、節點1 \(\cdots\)
1. 算法描述
本算法基於DFS,思路與傳統DFS基本類似,只不過在遍歷過程中對所經過的路徑通過一個棧進行保存,當找到回路時,檢測此條回路是否已經在結果集中出現,若未出現,則將其放入結果集。本過程中比較關鍵的兩步是DFS與結果集去重。
關於DFS。每當從一個新的頂點出發對其進行遞歸的深度遍歷時,我們維護一個新的節點已訪問數組visited[]
以及一個一維的回路棧loopStack
。當訪問到節點v時,若v已經被訪問,即visited[v]==true
時,掃描棧中是節點v是否已經出現過,當節點v已經在棧中出現過,則說明此時出現一條回路,我們將其加入結果集。若節點v未被訪問,此時置visited[v]=true
並將節點v入棧,並對v的所有鄰接點進行訪問,重復以上操作。到最深處(即已無鄰接點未遍歷)進行回溯處理,即將棧頂元素退棧。
關於去重。當需要在結果集中加入新找到的一條回路時,需要對結果集掃描,判斷此條回路是否已經出現過。但在一個有向圖中很容易發現回路\(0\rightarrow1\rightarrow2\rightarrow3(\rightarrow0)\)與回路\(2\rightarrow3\rightarrow1\rightarrow0(\rightarrow2)\)是兩條相同的回路,那么在結果集中我們需要對此類回路進行去重,此處我的具體做法是使用兩個指針i、j對將要比較的兩條回路同時進行掃描比較,指針i
指向第一條回路的起始位置,指針j
指向第二條回路中,與指針i指向位置元素相等的位置,記錄兩個回路中相等的元素個數count,當count==loop.size()
時,我們稱這兩條回路為同一條回路,否則將新回路加入結果集。
算法具體步驟:
- 從v出發對圖進行深度遍歷若此節點已訪問則轉2,否則轉3。
- 若此節點已經在loopStack中出現,表明有回路存在,判斷回路是否已經在結果集loopStacks中出現,若沒出現過,則放入結果集。
- 置Visited[v]=true,節點v入棧,對v的鄰接頂點繼續進行深搜。當搜索完所有鄰接頂點,棧頂元素退棧。
###2. 算法實現 完整代碼在github,請[點擊這里](https://github.com/geTiger/FindLoopsInGraph/blob/master/FindAllSimpleLoopsDFS.h) DFS部分實現 ``` void DFS(int v) { int position = FindInVector(loopStack, v); if (position ==-1 && visited[v]) visited[v] =false; if (visited[v] == 1) { if (position >=0) { vector
for (int j = 0; j < adjMatrixSize; j++) {
if (adjMatrix[v][j] == 1)
DFS(j);
}
loopStack.pop_back();
}
<br>
結果集去重部分
void AddLoopStack(vector
bool haveThisLoop = false;
int count,begin;
if (loopStacks.size() == 0)
loopStacks.push_back(loop);
else {
for (int i = 0; i < loopStacks.size(); i++) {//遍歷結果集中的每一個回路
count = 0;
begin = 0;
if (loop.size() == loopStacks[i].size()) {//若長度相等則進一步比較
//為方便比較,找到結果集中第i條回路中與loop進行匹配的起點
for (int k = 0; k < loopStacks[i].size(); k++) {
if (loop[0] == loopStacks[i][k])
begin = k;
}
//j指針從待添加結果集的loop數組的頭部開始掃描
//k指針從上述所找出的與loop數組比較的起點開始掃描
for (int j =0,k=begin; j < loop.size(); j++, k = (k + 1) % (loopStacks[i].size())) {
if (loop[j] == loopStacks[i][k])
count++;
}
if (count == loop.size()) {
//haveThisLoop = true;
//break;
return;
}
}
}//end else
loopStacks.push_back(loop);
}
}
###3. 算法復雜度分析
假設存在n個頂點,考慮最壞情況下,有向圖為有向完全圖,那么可能的回路個數就是$C_n^2+C_n^3+\cdots+C_n^n=2^n-n-1$個回路,另外需要對n個頂點均需要DFS,而每條邊都需要經過,加上去重的部分,所以時間復雜度為$O(n^32^n)$,需要保存所有回路的空間,故空間復雜度為$O(2^n)$。
<br>
<hr>
參考資料
嚴蔚敏數據結構(C語言版)