九章演算法筆記:Union find
阿新 • • 發佈:2019-01-05
Union find
有一顆二叉樹
C D
A B
C指向的結點D, A和B指向結點C 下圖 上一行是子節點 下一行是父親節點 如果要找A的big father的話,先從A找到C,再從C找到D
並查集 查詢 find 要O(n)的時間複雜度 union需要O(n)的時間複雜度
重要: 如果有一個連結串列,這個連結串列最後都指向最後一個元素,now you wanna find big father. now you set two pointers, now and temp
A—B—C—D (此時此刻now 指向A,temp指向B, 向右移動指標)
從第一個節點A開始遍歷,最後找打big father是D,這個時候 將A的next指向B
變成這樣的資料結構之後,find的時間複雜度就是O(1)
合併Union (找老大哥合併)
並查集的寫法有2種,第一種是程式碼比較多,但是比較好理解,這種方法在union方法裡面寫的比較多,在find寫的比少。只需要返回father陣列index對應的數值就可以了.
public class UnionFind {
int[] ids;
int cnt;
public UnionFind(int size){
this.ids = new int[size];
//初始化並查集,每個節點對應自己的集合號
for(int i = 0; i < this.ids.length; i++){
this.ids[i] = i;
}
this.cnt = size;
}
public boolean union(int m, int n){
int src = find(m);
int dst = find(n);
//如果兩個節點不在同一集合中,將兩個集合合併為一個
if(src != dst){
for(int i = 0; i < ids.length; i++){
if(ids[i] == src){
ids[i] = dst;
}
}
// 合併完集合後,集合數減一
cnt--;
return true;
} else {
return false;
}
}
public int find(int m){
return ids[m];
}
public boolean areConnected(int m, int n){
return find(m) == find(n);
}
public int count(){
return cnt;
}
}
第二種,就是類似於遞迴的寫法,這個方法不是遞迴,比遞迴寫法更容易理解。father陣列index對應的數並沒有改過來,只是通過改過的數去按圖索驥尋找最終的那個father
public class Solution {
public int longestConsecutive(int[] nums) {
UF uf = new UF(nums.length);
Map<Integer,Integer> map = new HashMap<Integer,Integer>(); // <value,index>
for(int i=0; i<nums.length; i++){
if(map.containsKey(nums[i])){
continue;
}
map.put(nums[i],i);
if(map.containsKey(nums[i]+1)){
uf.union(i,map.get(nums[i]+1));
}
if(map.containsKey(nums[i]-1)){
uf.union(i,map.get(nums[i]-1));
}
}
return uf.maxUnion();
}
}
class UF{
private int[] list;
public UF(int n){
list = new int[n];
for(int i=0; i<n; i++){
list[i] = i;
}
}
private int root(int i){
while(i!=list[i]){
list[i] = list[list[i]];
i = list[i];
}
return i;
}
public boolean connected(int i, int j){
return root(i) == root(j);
}
public void union(int p, int q){
int i = root(p);
int j = root(q);
list[i] = j;
}
// returns the maxium size of union
public int maxUnion(){ // O(n)
int[] count = new int[list.length];
int max = 0;
for(int i=0; i<list.length; i++){
count[root(i)] ++;
max = Math.max(max, count[root(i)]);
}
return max;
}
}
好好體會