今天要介紹一種非常重要的資料結構-並查集,這種資料結構常常被用於檢測有環圖的問題。
給定一些節點,我們想要確定這些節點哪些屬於同乙個子集,該怎麼做?現在假定這些節點之間通過邊來連線,我們嘗試用來表示一下。
我們可以看到一共有6個節點,其中0、1、2、3這幾個節點之間是連通的,4和5也是連通的。所以我們可以把上面6個節點分為兩個集合,集合1包含0、1、2、3共4個節點,集合2包含4和5兩個節點。
通過表示很直觀,但是,我們如何用**實現呢?
由上面的內容我們很容易知道,兩個節點如果屬於同一集合就能夠找到一條路徑連通這兩個節點,但是怎麼才能找到這條路徑呢?我們需要對這些節點進行等級劃分,每個節點只需要記住自己的上級節點即可,這樣的話兩個節點分別向上查詢,一直找到頂層的節點,如果頂層節點相同,我們就判定這兩個節點屬於同一子集,否則不是同一子集。等等,這不就是樹嗎?說的沒錯,維基百科中這樣定義並查集:在電腦科學中,並查集是一種樹型的資料結構,用於處理一些不交集(disjoint sets)的合併及查詢問題。怎麼樣,是不是感覺並查集的設計思路很精妙。
我們來思考一下並查集的**怎麼寫,首先咱們需要設定乙個parent
陣列用來儲存上級節點,初始時陣列中的所有值均為-1,表示沒有上級節點,我們先來寫一下這個方法。
private void init(int parent)
}
下乙個問題,怎麼尋找根節點?咱們看一下剛剛那個parent
陣列是怎麼用的。前面我們說過節點之間用邊來相連,parent
陣列用來儲存上級節點的下標。如下圖所示,2和3的上級節點是1,1的上級節點是0,0是根節點。同理,5的上級節點是4,4也是根節點,所以一共有兩個集合。
很容易發現,當parent
儲存的值為-1時表示這個節點就是根節點,我們用**實現一下尋找根節點的演算法。
private int findroot(int parent, int x)
return x_root;
}
上面說到尋找根節點的方法,下面咱們談談節點之間的邊是怎麼構建的。還是上面那幅圖,我們現在要把上面的兩個集合合併為乙個集合該怎麼做?很簡單,把其中乙個根節點的父節點設定為另乙個根節點,比如像下面這樣,把4的父節點設定為0。
在下面的**實現中,如果兩個要構建邊的節點在同一集合中就返回false,且不會建立這條邊,如果不是在同一集合中就返回true,然後它們就成了同一集合。
//返回true則合併成功,返回false則合併失敗
private boolean unionvertices(int parent, int x, int y) else
}
咱們看一下完整的**實現。
public class disjointsets }
private int findroot(int parent, int x)
return x_root; }
//返回true則合併成功,返回false則合併失敗
private boolean unionvertices(int parent, int x, int y) else }
public static void main(string args) , , , , , };
d.init(parent);
for (int i = 0; i < edges.length; ++i)
} system.out.println("no cycles found!");
}}
下面請大家考慮乙個問題,假設我們有10000個點,邊連線是, , ...
以此類推,那麼用上面的演算法我們將得到一條長長的鏈,也就是每次尋找根節點的時間複雜度為o(n
)o(n)
o(n)
,這時候我們就需要對演算法進行優化了。
我們來思考一下,當兩棵樹的高度不同時,應該怎麼進行合併呢?還是最上面兩棵樹的合併,咱們看一下下面兩種合併方式。
大家覺得第一種和第二種哪個更好?顯然第一種優於第二種,因為第一種是將高度較小的樹合併到高度較大的樹上的,這樣合併後的樹的高度仍然是原來較大的樹的高度,而相反樹的高度則會變大。簡單地說,如果使用第二種方式進行合併則會導致高度越來越大。
進行演算法優化時,我們需要設定另乙個陣列rank
,然後用這個陣列來儲存當前節點高度。實際需要修改的**只有init
和unionvertices
兩個方法,將原始的高度都設定為0,還有就是需要判斷兩棵樹的高度並進行比較。貼一下優化之後的**供大家與之前的**進行比較。
public class disjointsets }
private int findroot(int parent, int x)
return x_root; }
//返回true則合併成功,返回false則合併失敗
private boolean unionvertices(int parent, int rank, int x, int y) else else if (rank[x_root] < rank[y_root]) else
return true;
} }public static void main(string args) , , , , , };
d.init(parent, rank);
for (int i = 0; i < edges.length; ++i)
} system.out.println("no cycles found!");
}}
資料結構 並查集
並查集,顧名思義,合併 查詢 集合 並查集是一種樹型的資料結構,用於處理一些不相交集合 disjoint sets 的合併及查詢問題。常常在使用中以森林來表示。對於概念等等的這裡不再贅述,直接講解應用。應用1 判斷圖中有多少聯通分量 或者圖是否聯通 聯通分量 1 hdu 1213 應用2 判斷圖是否...
資料結構 並查集
time limit 1000ms memory limit 65536k 某城市有n個人,現在給定關於n個人的m條資訊,m條資訊是兩個人在同乙個小區,根據所給資訊,判斷這個城市最多可能有多少個小區。n個人編號為1 n。多組輸入。每組第一行有兩個整數n,m 2 n 50000,0 m n 2 接下來...
資料結構 並查集
一 基本概念 並查集是一種樹型的資料結構,用於處理一些不相交集合 disjoint sets 的合併及查詢問題。常常在使用中以森林來表示。集就是讓每個元素構成乙個單元素的集合,也就是按一定順序將屬於同一組的元素所在的集合合併。在一些有n個元素的集合應用問題中,通常是在開始時讓每個元素構成乙個單元素的...