並查集 你一看就明白就會用

2021-10-14 08:04:46 字數 3264 閱讀 9812

本文分成兩個部分,第一部分是基礎知識,這一塊這篇文章就講得非常好了,生動有趣又簡單易懂,是我見過的最好的演算法解說博文。既然它已經做到最好,我就沒必要做重複的事情了,就在這裡給大家點乙個方向。

我要做的是第二部分,就是給出題型模板與刷題。我們不能光會理論,不知道怎麼應用。

針對於並查集,我選的是力扣547——省份數量。這裡先給大家乙個截圖:

針對於這種相連與否的問題,應該想到並查集的方法

// 並查集模板

class disjointset

}// find函式有兩種寫法

// 寫法一

int find(int x)

int originalfather;

while (x != root)

}// 寫法二

int find(int x)

// union的寫法

void union2(int x, int y)

}bool same(int x, int y)

};int main()

這裡有幾點要注意:

2.大家在寫並查集的時候,union函式會用別的名稱,比如merge或者join,這是因為union是c++中的乙個關鍵字,我們命名的時候要小心,此處我命名為union2,以示區分。

3.在模板中,並查集的初始化是father[x]的每個元素等於其位置,比如father[i] = i。這是一種初始化的方案。可以這麼理解:在一開始,大家都沒有成立幫派,誰也不服誰,那就都自立門戶,自封「太祖」,所以就father[i] = i啦。那麼,除了這種初始化還有別的嗎?肯定是有的。比如把father定義成乙個雜湊表,然後用乙個特殊的value(比如-1)表明初始化的節點或者新加入的節點。這種做法可以參考力扣中的乙個題解。

4.顯然,並查集最重要的就是find和union的過程。union的時候,我們要找到各自節點的根節點之後union,不能:

void union2(int x, int y) 

}

這個錯誤現在看來有點愚蠢,但實際寫**的時候可能會疏忽,不容易發覺。

find的過程比union更複雜一些,為了達到更好的效果,我們希望進行路徑壓縮。那什麼是路徑壓縮呢?如果你在這裡有這樣的問題,那就說明你沒有好好看我開頭給的鏈結,這會兒趕快去看看:如果你覺得翻到上面比較麻煩,這裡我可以用飛雷神之術帶你去那裡。

在上面的模板中,我提供了兩種find函式的寫法。第一種比較中規中矩,先找到根,再進行路徑壓縮;第二種方法有遞迴的思想在其中,如何理解比較簡單?你可以把find(father[x])當做是找到並查集的根源,我們可以生動一點,叫做「太祖」。帶上了find()函式的頭銜,經過遞迴的過程,就可以找到太祖。所以第二種方法的意思就是:如果你是太祖(x == father[x],至於為什麼這樣就是太祖,那是因為並查集的初始化過程中我們給了每個節點「自立門戶」的機會,在一頓合併之後,還能當老大的就是「真·太祖」了。),那就可以離開函式;如果不是,那麼我們需要找到你的太祖,根據路徑壓縮的要求,我們希望找到的太祖就在你父節點的位置,那就是father[x] = find(father[x])啦,find(father[x])的find(·)就是乙個黑匣子,把father[x]放進去,你就可以得到太祖,然後請他做你的父節點。這樣一來,我們通過return的結果可以find到根節點也順便進行路徑壓縮。是不是感覺這樣的方法很簡潔?如果不能很好地理解,那麼還是用方法一,更加穩健。

至此,我們就分析完模板了,下面是力扣題。不過,在此之前,我還想再談談從並查集的設計思路上,我得到了什麼樣的啟發?

並查集的使用其實是利用了father陣列,設index為陣列中的索引,那麼就相當於構建了乙個對映:father[·],這樣乙個對映可以將index對映到index對應的根節點中。我們對於陣列,傳統的想法就是把一堆數字存在裡面,然後通過索引把它們調出來使用,但在並查集這裡,陣列起到的作用更像是乙個一元函式,具備對映的功能。如果可以從對映的角度理解並查集,應該能更容易明白其原理。

力扣547解答:

class disjointset 

}// int find(int x)

// int originalfather;

// while (x != root)

// return root;

// }

int find(int x)

void union2(int x, int y)

}bool same(int x, int y)

int getnums(int n)

return res.size();

}};class solution

}return unionfind.getnums(n); // 得到結果}};

這裡插播一條廣告

這裡寫一點本題的注意事項

1.bool same這個函式在這裡用不到。

2.unionfind.union2(i, j) 這裡可能對於一些同學來說有些抽象,因為i和j就是兩個數字,合起來代表的是矩陣中的乙個位置,可能有點難理解兩個數字union在一起是怎麼回事。其實你可以這麼看待這個問題:把i和j當成兩個城市,比如i代表廈門,j代表西安,isconnected[i][j]就代表這兩個城市是不是相連的。如果從具體事例了解,我相信有助於使用並查集的概念。

3.根據題目定義,這道題的矩陣肯定是乙個對稱矩陣,所以在第二層for迴圈上,可以進行些許優化;

至此,這道題目就算是做完了。怎麼樣?不難吧。

在未來的日子裡,希望與各位共同進步。這是我寫的第一篇關於演算法的博文,希望對你有一些幫助!

如果你覺得不錯,煩請點讚或者github加星(star),不勝感謝!

最後是鳴謝環節:

1.2.

我們下次見~

結尾:一些預告:

可能接下來有時間會寫關於摩爾投票法的csdn博文,這是乙個非常有趣的內容。如果你按奈不住激動的心情,想要先行了解,那麼可以直接在我的github中檢視,github的更新肯定比csdn要快得多~

至於未來還要做什麼,寫什麼,現在還沒有規劃,應該會寫自己感興趣的,有意思的東西。如果想要成體系地看歸類的題目,可以在我的github中檢視。

SpringAop切面程式設計(一看就會用)

可以在指定的某些類的某些方法的 前後或者 出現異常的時候,動態織入 指定的 1.aop aspect object programming 面向切面程式設計 功能 讓關注點 與業務 分離!2.關注點 重複 就叫做關注點 3.切面 關注點形成的類,就叫切面 類 把很多類中重複的 抽取出來的類,叫切面類...

KMP演算法最淺顯理解 一看就明白

kmp演算法看懂了覺得特別簡單,思路很簡單,看不懂之前,查各種資料,看的稀里糊塗,即使網上最簡單的解釋,依然看的稀里糊塗。我花了半天時間,爭取用最短的篇幅大致搞明白這玩意到底是啥。這裡不扯概念,只講演算法過程和 理解 字串匹配。給你兩個字串,尋找其中乙個字串是否包含另乙個字串,如果包含,返回包含的起...

KMP演算法最淺顯理解 一看就明白

目錄 說明 kmp演算法求解什麼型別問題 演算法說明 解析 kmp測試 複雜度分析 進一步說明2018 3 14 kmp演算法看懂了覺得特別簡單,思路很簡單,看不懂之前,查各種資料,看的稀里糊塗,即使網上最簡單的解釋,依然看的稀里糊塗。我花了半天時間,爭取用最短的篇幅大致搞明白這玩意到底是啥。這裡不...