連通性問題:
假如已知乙個整數對序列,每個整數代表某種型別的乙個物件,並且將p-q對解釋為p與q連通。假定連通可傳遞:如果p與q連通,q與r連通,那麼p與r連通。編寫乙個程式,從集合中過濾額外的連線對,如果前面的對表明p與q連通,則程式應該忽略p-q,並繼續輸入下乙個對。
例如 :
- input output
- 3-4 3-4
- 4-9 4-9
- 8-0 8-0
- 2-3 2-3
- 5-6 5-6
- 2-9 (put out nothing because 2-3-4-9)
- 5-9 5-9
我們可以先將問題圖形化表示,先看乙個樹狀圖。(《algorithms in c》)
快速查詢(慢速並集)的思路很簡單:只要兩個陣列有相同的元素出現,那麼就可以認為這兩個陣列連通,可以把它歸併到一起,如上圖所示。
如果用**實現歸併呢?很簡單,只需要乙個陣列id,如果p與q連通,那麼讓id[p] = id[q],當且僅當id[p] == id[q]的時候,p與q連通。
#include
#define n 1000
//quick-find
int main()
while(scanf("%d %d",&p,&q) == 2)
t = id[p];
for(i = 0;i < n;i ++)
} printf("%d %d\n",p,q);}}
性質:對於具有n個物件,每個物件包括m個並集運算的連通性問題,快速查詢的演算法至少要執行mn條指令。
方案一中的查詢每一次都遍歷了陣列id的全部,效率要低很多,方案二提供了乙個更好的思路。
圖中表述的連通分量叫做樹,每棵樹只有乙個指向它本身的物件,即樹根,其餘的物件均指向或者間接指向樹根。比如一棵樹的根是9,那麼id[9] == 9,如果3->4,4->9,即id[3] == 4,id[4] == 9,3和4均不指向它本身,最終的指向都為9,3和4都在以9為根節點的書裡面。
這樣我們只需要找到樹的乙個節點,就可以根據這個節點找到樹根,從而判斷是否具有連通性了。
#include
#define n 1000
//quick-union
int main()
/*p q 0 1 2 3 4 5 6 7 8 9
3 4 0 1 2 4 4 5 6 7 8 9
4 9 0 1 2 4 9 5 6 7 8 9
8 0 0 1 2 4 9 5 6 7 0 9
2 3 0 1 9 4 9 5 6 7 0 9
5 6 0 1 9 4 9 6 6 7 0 9
2 9 0 1 9 4 9 6 6 7 0 9
5 9 0 1 9 4 9 6 6 7 0 9
*/while(scanf("%d %d",&p,&q) == 2)
id[i] = j; //p和q的樹根不相同,則把q的樹根賦給p的樹根,那麼這兩棵數就建立了聯絡變成了一棵樹
printf("%d %d\n",p,q); }}
性質:若m>n,快速並集演算法可能要執行多於m*n/2條指令來解決乙個擁有n個物件、m個對的連通性問題。
有了以上兩個演算法做鋪墊,加權快速演算法也就不難理解了,加權快速演算法是快速並集演算法的修改版:不是任意連通第二棵樹和第一棵樹,而是比較兩棵樹節點數多少,把小的樹的根節點連到大的樹的根節點上。這樣每個節點與樹根的距離短,查詢的運算效率也就高了。
**如下:
#include
#define n 1000
//weighted quick-union
int main()
while(scanf("%d %d",&p,&q) == 2)
if(size[i] < size[j])
else
printf("%d %d\n",p,q);}}
性質:加權快速並集演算法判斷n個物件的其中兩個是否連通,最多要跟蹤2*lgn個指標。
加權快速並集演算法最壞的情況是,每個並集運算連線相等大小樹。如果物件的數量小於2^n,則從任意節點到樹根的距離小於n。
現在我們發現,我們的演算法已經優化了很多,我們來討論這樣乙個問題:能否有乙個確保線性效能的演算法?
理想情況下,我們希望每乙個節點都直接指向根節點,而不希望付出變換大量指標的代價。其實這個很容易實現,我們可以通過修改樹的結構來達到這樣的目的。這種方法叫做路徑壓縮(path compression)。
實現方法是新增另一遍次經過每條路徑,把每個路徑id設定為指向根。最終結果是我們會看到乙個扁平的樹,接近快速查詢演算法的理想情況。
**如下:
#include
#define n 1000
//weighted quick-union with path compression by halving
int main()
while(scanf("%d %d",&p,&q) == 2)
for(j = q;id[j] != j;j = id[j])
if(i == j)
if(size[i] < size[j])
else
printf("%d %d\n",p,q);}}
《演算法》筆記 2 動態連通性問題
在基礎部分的最後一節,作者用乙個現實中應用非常廣泛的案例,說明以下幾點 動態連通性問題的輸入是一列整數對,其中的每個整數都表示乙個某種型別的物件,一對整數對p q可以理解為 p和q是相連的 相連是一種等價關係,其具有 程式的目標是過濾掉序列中無意義的整數對,當程式從輸入中讀取了整數對pq時,如果已知...
並查集 解決連通性問題
給的輸入形式不同,但都是利用並查集,並查集的實現是不變的。1 323.無向圖中連通分量的數目 給定編號從 0 到 n 1 的 n 個節點和乙個無向邊列表 每條邊都是一對節點 請編寫乙個函式來計算無向圖中連通分量的數目。2 547.省份數量 連通分量的數目 有 n 個城市,其中一些彼此相連,另一些沒有...
Sicily 連通性問題 演算法逐層優化
description 關係r具有對稱性和傳遞性。數對p q表示prq,p和q是0或自然數,p不等於q。要求寫乙個程式將數對序列進行過濾,如果乙個數對可以通過前面數對的傳遞性得到,則將其濾去。例如 輸入 輸出 連通性 3 4 3 4 4 9 4 9 8 0 8 0 2 3 2 3 5 6 5 6 2...