並查集是一種樹型的數據結構,用於處理一些不相交集合的合併及查詢問題。經常在使用中以森林來表示。python
並查集有三種基本操做,得到根節點,判斷兩節點是否連通,以及將兩不連通的節點相連(至關於將兩節點各自的集合合併)數組
用UnionFind類來表示一個並查集,在構造函數中,初始化一個數組parent,parent[i]表示的含義爲,索引爲i的節點,它的直接父節點爲parent[i]。初始化時各個節點都不相連,所以初始化parent[i]=i,讓本身成爲本身的父節點,從而實現各節點不互連。數據結構
def __init__(self, n): self.parent = list(range(n))
因爲parent[i]僅表示本身的直接父節點,查詢兩個節點是否相交須要比較它們的根節點是否相同。所以要封裝一個查詢本身根節點的方法。函數
def get_root(self, i): while i != self.parent[i]: i = self.parent[i] return i
接下來能夠經過來比較根節點是否相同來判斷兩節點是否連通。性能
def is_connected(self, i, j): return self.get_root(i) == self.get_root(j)
當要連通兩個節點時,咱們要將其中一個節點的根節點的parent,設置爲另外一個節點的根節點。注意,連通兩個節點並不是僅僅讓兩節點自身相連,其實是讓它們所屬的集合實現合併。優化
def union(self, i, j): i_root = self.get_root(i) j_root = self.get_root(j) self.parent[i_root] = j_root
接下來咱們作兩個小優化。
因爲調用get_root時須要經過不斷找本身的直接父節點,來尋找根節點,若是這棵樹的層級過深,會致使性能受到嚴重影響。所以咱們須要在union時,儘量的減少合併後的樹的高度。
在構造函數中新建一個數組rank,rank[i]表示節點i所在的集合的樹的高度。code
所以,當合並樹時,分別得到節點i和節點j的root i_root和j_root以後,咱們經過訪問rank[i_root]和rank[j_root]來比較兩棵樹的高度,將高度較小的那棵連到高度較高的那棵上。若是高度相等,則能夠隨便,並將rank值加一。遞歸
def union(self, i, j): i_root = self.get_root(i) j_root = self.get_root(j) if self.rank[i_root] == self.rank[j_root]: self.parent[i_root] = j_root self.rank[j_root] += 1 elif self.rank[i_root] > self.rank[j_root]: self.parent[j_root] = i_root else: self.parent[i_root] = j_root
經過對union操做的改良能夠防止樹的高度太高。咱們還能夠對get_root操做自己進行優化。
當前每次執行get_root時,須要一層一層的找到本身的父節點,很費時。因爲根節點沒有父節點,而且文章開始處提到過若是一個節點沒有父節點,那麼它的父節點就是本身,所以能夠說只有根節點的父節點是本身自己。如今咱們加上一個判斷,判斷當前節點的父節點是否爲根節點,若是不爲根節點,就遞歸地將本身的父節點設置爲根節點,最後返回本身的父節點。索引
def get_root(self, i): if self.parent[i] != self.parent[self.parent[i]]: self.parent[i] = self.get_root(self.parent[i]) return self.parent[i]
以上是python實現一個簡單的並查集的方式。get