本文參考自《演算法筆記》並查集篇
什麼是並查集?並查集可以理解為是一種維護資料集合的結構。名字中並查集分別取自於單詞並(union合併),查(find查詢),集(set集合)。乙個並查集需要有一下兩個功能:
並查集主要是用乙個陣列來實現的,也就是乙個father[n]
,我們定義father[i]
表示i的父親結點。父親結點也是存在於這個集合當中。同時,如果father[i] == i
的話,我們也說father[i]
是i的根結點。乙個集合裡面同屬的集合裡面只有乙個根結點。我們可以看看下面的示例
father[1] = 1; //結點1的父親結點是1,同時也說1號是這個集合裡面的根結點
father[2] = 1;//結點2的父結點是1
father[3] = 2; //結點3的父結點是2
father[4] = 2;//結點4的父結點是2
father[5] = 5; //結點5的父親結點是5,同時也說5是這個集合裡面的根結點
father[6] = 6; //結點6的父結點是5
在上面中,根結點同屬1的我們視為乙個集合,根結點為5的,我們視為乙個集合,這樣我們就得到了兩個集合。
首先我們對並查集需要進行初始化的操作,然後才能根據需要進行合併或者查詢的操作。
一開始每個元素都是乙個集合,所以需要對陣列進行father[i] = i
的操作
for(int i = 1; i <= n; i++)
由於我們說過了同乙個集合只有乙個根結點,因此查詢就是在乙個集合中查詢到根結點的操作。實現方式可以是遞迴或者遞推,思路都是一樣,就是反覆尋找父親結點。
先來看遞推的**:
//findfather函式返回元素x所在集合的根結點
int findfather(int x)
return x;
}
我們以圖1為例,按照上面的遞推方法,走一下查詢元素4的根結點的流程:
再來看遞迴的**:
int findfather(int x)
合併就是將兩個集合合併成乙個集合,比如說題目中一般給出兩個元素說這兩個是乙個集合,那麼就需要將其合併。具體實現上一般是先判斷兩個元素是否屬於同乙個集合,只有當兩個元素屬於不同集合才能合併。而合併的過程就是乙個元素的根結點的父親結點指向另乙個元素的根結點。
具體思路主要是以下兩個步驟
對於給定的兩個元素,判斷是否屬於乙個集合,可以呼叫上面的查詢元素,分別查詢其的根結點,然後判斷根結點是否相同。
合併兩個集合,在步驟1中我們已經找到兩個元素的根結點,如果兩個根結點不相同的話,我們將元素a的根結點fa,元素b的根結點fb,令father[fa] = fb,當然反過來也是可以的,father[fb] = fa。這樣子就合併了兩個集合。
比如我們合併元素4和6的兩個集合,找到它們的根結點,然後進行合併。如圖操作後
具體實現**如下
void union(int a,int b)
}
合併的過程,只是對兩個不同的集合進行合併,如果兩個元素在相同的集合中,那麼就不會對它們進行操作。這就保證了同乙個集合中一定不會產生環,即並查集每乙個集合都是一棵樹。
像上面的查詢函式是沒有經過優化的,在極端的情況下效率極低。比如,題目給出的元素很多形成一條鏈,那麼這個查詢的函式效率就會非常低。如圖所示,總共有105個元素形成一條鏈,那麼假設要進行105次查詢,且每次查詢都要查詢最後面的結點的根結點,那麼每次都要花費10^5的計算量查詢,這顯然無法接受。
那麼該如何優化呢?
我們以下面這個為例子
father[1] = 1;
father[2] = 1;
father[3] = 2;
father[4] = 2;
由於是查詢根結點,所以我們可在查詢的過程中,等價操作於
father[1] = 1;
father[2] = 1;
father[3] = 1;
father[4] = 1;
變化的過程如圖
具體做法就是
按照原先的寫法獲得x的根結點r
重新從x開始走一遍尋找根結點的過程,把路徑上經過的所有結點的父親全部改為根結點r
由此我們可以寫出**:
(使用遞推的方式)
int findfather(int x)
//到這裡,x存放的是根結點,下面把路徑上的所有結點的father都改成根結點
while(a != father[a])
return x; //返回根結點
}
(使用遞迴的方式)
int findfather(int v)
}
並查集的基本使用方式就這些,具體的變形還需要具體情況來視。下面給出一道題目可以練練手
leetcode990.等式方程的可滿足性
模版 並查集
什麼是並查集 並查集是一種樹型的資料結構,用於處理一些不相交集合 disjoint sets 的合併及查詢問題。為何使用inline inline是c 關鍵字,在函式宣告或定義中,函式返回型別前加上關鍵字inline,即可以把函式指定為內聯函式。這樣可以解決一些頻繁呼叫的函式大量消耗棧空間 棧記憶體...
並查集 模版
並查集 並查集,在一些有n個元素的集合應用問題中,我們通常是在開始時讓每個元素構成乙個單元素的集合,然後按一定順序將屬於同一組的元素所在的集合合併,其間要反覆查詢乙個元素在哪個集合中。這一類問題近幾年來反覆出現在資訊學的國際國內賽題中,其特點是看似並不複雜,但資料量極大,若用正常的資料結構來描述的話...
演算法模版之並查集
簡介 並查集實質是一種樹形的資料結構,一般用於處理一些不相交集合的合併及查詢問題。在具體問題解決上,比如找公共祖先節點 檢查圖的連通性等等,用通俗點的語言來描述的話,例如乙個幫派 有點社會呀 a的大哥是b,b的大哥是c,c的大哥是d,此時a b c d互相是不認識的,假如發生了幫派爭鬥,b和d相遇了...