程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> 並查集實現Tarjan算法,集實現Tarjan算法

並查集實現Tarjan算法,集實現Tarjan算法

編輯:C++入門知識

並查集實現Tarjan算法,集實現Tarjan算法


本文是對http://noalgo.info/476.html的一點理解,特別是對其中

int father[mx];  //節點的父親
int ancestor[mx]; //已訪問節點集合的祖先

這兩個數組作用的解釋;

首先必須明確,並查集重建的樹跟原來的樹並不一樣;

還是借用該文章的例子:

Tarjan算法是基於DFS(深度優先搜索), 圖中的樹深度優先遍歷的順序應該是:

4->7->5->1->2->6->3->0

但作者卻說,節點的處理順序為:

4->7->5->1->0->2->6->3

其實, 這裡第一種順序是我們處理查詢請求的順序,例如我們遍歷到5, 則我們可以獲得 5與5之前的所有已經遍歷過的節點即(5,4) (5,7)的查詢結果; 

第二種順序則是我們建立並查集的順序

關鍵代碼如下:

 1 void Tarjan(int x) //Tarjan算法求解LCA
 2 {
 3     for (int i = 0; i < tree[x].size(); i++)
 4     {
 5         Tarjan(tree[x][i]);         //訪問子樹
 6         unionSet(x, tree[x][i]); //將子樹節點與根節點x的集合合並,這裡是並查集處理節點x 
 7         ancestor[findSet(x)] = x;//合並後的集合的祖先為x
 8     }
 9     vs[x] = 1; //標記為已訪問, 這裡是DFS遍歷節點x
10     for (int i = 0; i < query[x].size(); i++) //與根節點x有關的查詢
11         if (vs[query[x][i]]) //如果查詢的另一個節點已訪問,則輸出結果
12             printf("%d和%d的最近公共祖先為:%d\n", x, 
13                     query[x][i], ancestor[findSet(query[x][i])]);
14 }

兩者順序不同的原因在於下面的第6行和第9行代碼,我們在還未遍歷父節點的時候,處理第一個子樹後,父節點就已經在並查集內了;例如我們還沒有遍歷節點1,在遍歷節點4後,就會處理節點4與其父節點1的合並;

 

下面,我們來慢慢建立並查集的樹;

第一步:

遍歷過的元素{4}, 集合[4]{4}->4

第二步:

集合{4}與父節點{1}按秩合並, 合並後的集合為{4,1},集合代表元素為4,即father[4] = 4, father[1] = 4; 集合{4,1}的公共祖先為1,ancestor[4] = 1; 即這個集合的代表元素並不是它的公共祖先

第三步:

遍歷過的元素有{4,7}, 有兩個集合[4]{4,1}和[7]{7},  ([]裡面的元素為代表元素,{}的元素為集合內的所有元素), 此時, 若查詢(7,4), 則4已經遍歷過, 訪問4所在集合的代表元素為 father[4],  集合4的公共祖先為ancestor[father[4]]

第四步:

遍歷過的元素有{4,7,5}, 集合[7]{7}合並集合[5]{5}為[7]{7,5}, ancestor[7] = 5

.....

遍歷完根節點的第一棵子樹後, 集合[7]{4,1,5,7} 與根節點集合[0]{0}合並為[7]{0,1,4,5,7}, 即father[0]=7, 同時更新集合的公共祖先ancestor[7]=0;

最後遍歷過的元素{4,7,5,1,2,6,3,0}, 集合為[7]{4,7,5,1,2,6,3,0}, ancestor[7] = 0

為方便理解,最後的圖是沒有經過路徑壓縮的, 實際上應該是所有元素的父節點皆為集合代表元素7

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved