並查集的實例以及應用

博客主要參考:https://baike.so.com/doc/6119935-6333082.html 

1.概念與運用

並查集是一種樹型的數據結構,用於處理一些不相交集合(Disjoint Sets)的合併及查詢問題,常常在使用中以森林來表示。主要運用在查找無向圖的連通性問題上(連通分支個數問題、種類問題等),不管如何出現,只要可以抽象成無向圖的連通性問題,均可以運用。

2.具體實例

實例來自飄過的小牛中的博客:

話說江湖上散落着各式各樣的大俠,有上千個之多。他們沒有什麼正當職業,整天揹着劍在外面走來走去,碰到和自己不是一路人的,就免不了要打一架。但大俠們有一個優點就是講義氣,絕對不打自己的朋友。而且他們信奉“朋友的朋友就是我的朋友”,只要是能通過朋友關係串聯起來的,不管拐了多少個彎,都認爲是自己人。這樣一來,江湖上就形成了一個一個的幫派,通過兩兩之間的朋友關係串聯起來。而不在同一個幫派的人,無論如何都無法通過朋友關係連起來,於是就可以放心往死了打。但是兩個原本互不相識的人,如何判斷是否屬於一個朋友圈呢?

我們可以在每個朋友圈內推舉出一個比較有名望的人,作爲該圈子的代表人物。這樣,每個圈子就可以這樣命名“中國同胞隊”美國同胞隊”……兩人只要互相對一下自己的隊長是不是同一個人,就可以確定敵友關係了。

但是還有問題啊,大俠們只知道自己直接的朋友是誰,很多人壓根就不認識隊長要判斷自己的隊長是誰,只能漫無目的的通過朋友的朋友關係問下去:“你是不是隊長?你是不是隊長?”這樣,想打一架得先問個幾十年,餓都餓死了,受不了。這樣一來,隊長面子上也掛不住了,不僅效率太低,還有可能陷入無限循環中。於是隊長下令,重新組隊。隊內所有人實行分等級制度,形成樹狀結構,我隊長就是根節點,下面分別是二級隊員、三級隊員。每個人只要記住自己的上級是誰就行了。遇到判斷敵友的時候,只要一層層向上問,直到最高層,就可以在短時間內確定隊長是誰了。由於我們關心的只是兩個人之間是否是一個幫派的,至於他們是如何通過朋友關係相關聯的,以及每個圈子內部的結構是怎樣的,甚至隊長是誰,都不重要了。所以我們可以放任隊長隨意重新組隊,只要不搞錯敵友關係就好了。於是,門派產生了。

3.具體實現(固定)

//在地圖上給你若干個城鎮,這些城鎮都可以看作點,然後告訴你哪些對城鎮之間是有道路直接相連的。最後要解決的是整幅圖的連通性問題。輸入數據如下:
//第一行:4 2 
//第二行:1 3 
//第三行:4 3
//第一行告訴你,一共有4個點,2條路。下面兩行告訴你,1、3之間有條路,4、3之間有條路
class solution{
    int getRoot(vector<int> nums, int root){
        int son = root;
        while (root != nums[root]){ //查找根節點
            root = nums[root];
        }
        while (son != root){ //路徑壓縮
            int t = nums[son]; //直接根節點
            nums[son] = root;
            son = t;
        }
        return root;
    }
    
    int buildConnection(int n, int m, vector<vector<int>> edges){  //參數取決於題目,這裏假設是無向圖的連通問題
        vector<int> nums(n);
        for (int i = 0; i < n; ++i) nums[i] = i;
        for (auto edge: edges){
            int a = getRoot(nums, edge[0]);
            int b = getRoot(nums, edge[1]);
            if (a != b){
                nums[a] = b;
            }
        }
    }
}

路徑壓縮是特別重要的,可以解決快速查找根節點的問題:

[1]https://blog.csdn.net/niushuai666/article/details/6662911

[2] https://baike.so.com/doc/6119935-6333082.html

發佈了155 篇原創文章 · 獲贊 2 · 訪問量 2355
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章