演算法與資料結構(六)並查集

2021-09-20 04:50:04 字數 4221 閱讀 2598

圖相關演算法的實現。

一種不一樣的樹形結構

連線問題 connectivity problem

視覺化的來看連線問題:

連線問題

左上右下是否是連線的呢?

意義:實際應用中的作用

社交網路:facebook中使用者a和b中的聯絡(好友關係)。是否能聯絡到。

**電影書籍,多**之間形成網路。

網際網路網頁之間形成的網路

路由器和路由器之間形成的也是網路

道理交通,航班排程都是網路

數學中的集合類實現

並就是實現並集。& 查詢

連線問題 & 路徑問題

比路徑問題要回答的問題少(路徑是什麼,連線問題只問有沒有連)

- 和二分查詢作比較:順序查詢法順便回答了rank。和前面其他元素的位置

- 和堆作比較:只關心最大最小。

除了回答問題本身之外是不是額外的回答了別的問題。很有可能就存在

更高效的演算法。:因為高效演算法不需要回答額外的問題。

對於一組資料,主要支援兩個動作:

- union( p , q )

- find( p )

用來回答乙個問題

- isconnected( p , q )
最簡單的表示方式;

陣列。0,1.

0-4 5-9

0-4是一組,5-9是一組。組內之間有聯絡

奇偶

奇數是一組,偶數是一組。

namespace uf1 

~unionfind()

//傳入元素p,返回元素對應的id。

int find(int p)

bool isconnected(int p, int q)

//傳入兩個元素,並

void unionelements(int p, int q)

};}

testhelper.h:

namespace unionfindtesthelper

for(int i = 0 ; i < n ; i ++ )

time_t endtime = clock();

cout<<"uf1, "<<2*n<<" ops, "int main()

執行結果:

快速查詢,並很慢

quick find 查詢時只需要o(1)級別。但是並確很慢

並查集的另一種實現思路

常規實現思路

將每乙個元素,看做是乙個節點。

元素節點

每個元素擁有乙個指向父節點的指標。然後最上面的父節點指標指向自己。

quick union

陣列存放父親

parent(i) = i;

初始狀態

union 3 4

union 3 4

union 3 8

union 3 8

union 6 5

union 6 5

union 9 4

union 9 4

要將9連線到4的根節點8上去。陣列中:4-3-8-8 8是4的根節點。9指向8.

4和9連線在一起:因為根相同。

成果

其中6和2連線是6的根0和2的根1選取了1將0掛上。

namespace uf2

~unionfind()

//不斷向上找父親

int find(int p)

//看是否能找到同樣的根

bool isconnected( int p , int q )

//找到p的根,和q的根

void unionelements(int p, int q)

};}

執行結果:

執行結果

當n大的時候,方法1更優了。

並查集的優化

問題:union 9,4 & union 4 9

union 9 4

9的元素少,將它指向4的根節點。形成的樹層數低。

// 我們的第三版union-find

namespace uf3

}// 析構函式

~unionfind()

// 查詢過程, 查詢元素p所對應的集合編號

// o(h)複雜度, h為樹的高度

int find(int p)

// 檢視元素p和元素q是否所屬乙個集合

// o(h)複雜度, h為樹的高度

bool isconnected( int p , int q )

// 合併元素p和元素q所屬的集合

// o(h)複雜度, h為樹的高度

void unionelements(int p, int q)

else}};

}

執行結果:

執行結果

優化相當明顯。

int main()
100萬在1秒之內

union 4 2

合併4和2

依靠集合的size來決定誰指向誰並不完全合理。根據層數才最合理

基於rank的優化

namespace uf4

}// 析構函式

~unionfind()

// 查詢過程, 查詢元素p所對應的集合編號

// o(h)複雜度, h為樹的高度

int find(int p)

// 檢視元素p和元素q是否所屬乙個集合

// o(h)複雜度, h為樹的高度

bool isconnected( int p , int q )

// 合併元素p和元素q所屬的集合

// o(h)複雜度, h為樹的高度

void unionelements(int p, int q)

else if( rank[qroot] < rank[proot])

else}};

}

// 對於uf3來說, 其時間效能依然是o(n*h)的, h為並查集表達的樹的最大高度

// 但由於uf3能更高概率的保證樹的平衡, 所以效能更優

unionfindtesthelper::testuf3(n);

// uf4雖然相對uf3進行有了優化, 但優化的地方出現的情況較少,

// 所以效能更優表現的不明顯, 甚至在一些資料下效能會更差

unionfindtesthelper::testuf4(n);

執行結果

路徑壓縮(path compression)

前面我們都在優化union。其實find我們也可以進行優化。

1我們要find4

4去連線它爺爺

4去連線它爺爺。下面考查2.

2連線爺爺

路徑壓縮

int find(int p)

}

最優的情況

寫乙個遞迴的函式:呼叫findx,返回的就是x節點的根。讓每個parentx指向findx的結果。findx的結果也是findparentx的結果。找x的時候,將x的findparent的結果,指向父親的結果。

**:

//             path compression 2, 遞迴演算法

if( p != parent[p] )

parent[p] = find( parent[p] );

return parent[p];

優化情況並不明顯。甚至因為遞迴的消耗,。所以理論最優不一定實際好。

並查集的操作,時間複雜度近乎是o(1)的。

- 經過路徑壓縮:近乎1就能到根節點。

解決圖,網路連線。最短路徑。路徑是什麼?都得使用圖論。

資料結構與演算法之並查集

並查集結構可以用於 1 檢查兩個元素是否屬於同乙個集合 比如對於圖1這個例子來說,如果我們想要檢查節點d和節點e是否屬於同乙個集合,可以這樣操作 d節點往上找其父節點,一直往上找,直到某個節點的父節點是其本身,此時停止 找到了節點a e節點也按照相同的步驟往上找其父節點,找到節點a 如果這兩個節點往...

資料結構與演算法之並查集

並查集 union find 是一種高效的資料結構,主要的操作有 為方便敘述,把所有元素視作點,元素之間的關係視作線,存在聯絡便存在關係 需要注意的是,這裡的關係應當是1.自反的,2.對稱的,3.傳遞的 所謂合併,便是將兩個點之間 畫 一條線。又上邊的定義不難理解相連的若干點之間互相存在關係,這樣我...

演算法與資料結構 並查集 Disjoint Set

並查集 disjoint set 用來判斷已有的資料是否構成環。在構造圖的最小生成樹 minimum spanning tree 時,如果採用 kruskal 演算法,每次新增最短路徑前,需要先用並查集來判斷一下這個路徑是否會構成環。遍歷圖的每一條邊,按照下面的原則將對應的兩個頂點新增到集合中 為了...