在poj根據前人的分類,做了很多並查集的題目,如果知道是應用並查集的話,建模和code起來已經比較熟練了,不過不知在遇到陌生題時是否能發現它是用並查集來完成。今天來總結下(要在做了些並查集的題目後才能理解)。做得題目有1182,1611,1703,1988,2236,2492等等,還有一些很基礎的。
我是從食物鏈那道經典題入手的(
),讀懂別人寫的**後就基本理解並查集的組織與應用了,因為這道題很典型也比一般的複雜。
並查集一般就三個操作:初始化,查詢,合併。一般都是用乙個一維陣列來組織(實際是一棵樹,每個結點都指向父節點,用根節點來標誌集合),陣列所存內容為該下標表示點的父節點。
先講下對於並查集三個操作中必須的部分:初始化函式一般沒有什麼變化,都是將每個結點的父節點初始化為本身;查詢函式一般都是遞迴的尋找父節點直至根結點,返回根結點的值來標識集合;合併函式一般就將兩個集合中乙個根結點的父節點設為另乙個根結點。
這樣最基本的並查集就構成了,但是對於具體問題尤其是一些複雜問題必須要加入一些操作:
1.路徑壓縮
即在查詢操作時,將查詢的結點到根結點路徑上的所有結點都掛到根結點上,這樣就保證了樹的高度不會很高,節省了查詢時間。
2.附加型別標識(這是我的叫法哈)
就是對於每乙個點還包括乙個型別標識,在有些問題時是必需的(例如食物鏈),並查集中兩個點同屬於乙個集合表明已明確了這兩點的關係,關係通過型別標識來區分(往往是相對的),那在加入了型別標識的並查集中就必須豐富那三個基本操作:
1.首先初始化時將每個點的型別標識初始化為0,這裡初始化為0我認為是最優的,這能保證每個集合的根結點的型別標識都為0,以方便後面的操作(需要具體做題來體會哈);
2.對於合併操作,要修改被合併的根結點的型別標識(即合併後它不再是根結點的點),要保證這個點的型別標識在合併後的集合中是準確的(而這個點的後繼的型別標識將放在查詢操作中修改,因為並查集集合是無法訪問到後繼的,只能由葉結點向根結點遍歷),那修改的依據是什麼呢?假設所給新的關係涉及兩個點a,b,它們的根結點是ra,rb,那依據有三個:1.ra和a的相對關係,2.rb和b的相對關係,3.新給出的a和b的關係。這樣就可以得出ra和rb的關係(至少做到的題中都能根據這三個條件推出,這是必須的,如果不能推出則要修改並查集的結構了),即rb型別標識的新值(如果是把rb所在集合合併到ra所在集合的話);
3.對於查詢操作,正如前面所說,我們只要在壓縮路徑時,從路徑中深度最低的點(這個順序也很關鍵)根據當前點和當前點父節點的型別標識更新當前點的型別標識即可,這樣能保證查詢後,該路徑上所有的型別標識都正確。那為什麼根據當前點和當前點父節點的型別標識就能獲得正確的型別標識呢?因為在合併時,被合併集合的根結點加入新集合後的型別標識是正確的,而因為集合根結點的型別標識都為0,所以加入新集合後原集合非根節點的型別標識加上原來其根結點的新型別標識的值即可得到正確的值,且此操作必須按深度遞增的方向進行(這裡很難理解,需通過例項才能明白哈,畫個圖能幫助理解)。
掌握以上的方法,一般並查集的題目應該很熟練了。
並查集 總結
自己學完後總結一下吧,並查集,我的理解就是乙個查詢與合併,用乙個find函式來查詢自己的祖先也就是根節點,在這過程中還可以進行路徑壓縮,就是讓這個點直接變到根節點之下,還有就是合併,找出這兩個點的根節點,如果根節點不相同的話,就將乙個根節點放到另乙個根節點下面。這道題的就是用並查集做,找出總共有幾個...
並查集總結
用自己的理解來寫好了 有些地方可能不對 不定時更新理解 操作主要為三步 一 初始化 void init 二 查詢 這個為路徑壓縮 就是每次查詢時更新 查詢節點 x 上面所有節點直接使他們連向根節點 每次查詢更新 int find int x 另一種路徑壓縮 不過麻煩了些 作為理解 int find ...
並查集總結
注 此博文是在老師上課之後總結的,屬於課堂筆記,大部分摘自老師提供的課件。並查集總結總結兩點就是 並 和 查 並查集是一種樹型的資料結構,用於處理一些不相交集合 disjoint sets 的合併及查詢問題。常常在使用中以森林來表示。集就是讓每個元素構成乙個單元素的集合,也就是按一定順序將屬於同一組...