通過DFS求解有向圖(鄰接矩陣存儲)中所有簡單回路


前言

查閱了網上許多關於通過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()時,我們稱這兩條回路為同一條回路,否則將新回路加入結果集。

算法具體步驟:

  1. 從v出發對圖進行深度遍歷若此節點已訪問則轉2,否則轉3。
  2. 若此節點已經在loopStack中出現,表明有回路存在,判斷回路是否已經在結果集loopStacks中出現,若沒出現過,則放入結果集。
  3. 置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 loop; //將環單獨拿出並放入結果集 for (int i = position; i < loopStack.size(); i++) { loop.push_back(loopStack[i]); } AddLoopStack(loop); return; } return; } visited[v] = true; loopStack.push_back(v);
for (int j = 0; j < adjMatrixSize; j++) {
	if (adjMatrix[v][j] == 1)
		DFS(j);
}

loopStack.pop_back();

}

<br>

結果集去重部分

void AddLoopStack(vector loop) {
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語言版)


免責聲明!

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



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