並查集(Union Find)

ice_moss發表於2021-05-21

一、概念及其介紹

並查集是一種樹型結構,用於處理一些不相交集合的合併及查詢問題。
並查集的思想是用一個陣列表示了整片森林(parent),樹的根節點唯一標識了一個集合,我們只要找到了某個元素的的樹根,就能確定它在哪個集合裡。
對於並查集主要支援兩個操作:

  • 並{ union(p,q) }
  • 查詢{ find(p) }
    來回答一個問題:

    連線{ inConnected(p, q) }

二、並查集的基本資料表示

難點分析:橫向上的數值其實是對應橫線下資料的在陣列中的索引值,也就是說橫線下是一個真正的陣列,而橫線上則是陣列對應的索引,在這裡我們是用索引當作元素,用陣列資料值的異同來表示元素是否連線。

橫線上:用陣列索引表示元素
橫線下:表示連線情況(值為0的表示在一個集合即連線)
所以0-4在同一個集合,5-9在同一個集合:
並查集(Union Find)

三、程式碼實現

下面我們來介紹並查集的主要操作:
我們先實現一個並查集:

#include <iostream>
#include <cassert>

using namespace std;

  // 我們的第一版Union-Find
namespace UF1 {

class UnionFind {

private:
  int *id;         // 第一版Union-Find本質就是一個陣列
  int count;     // 資料個數

 public:
   // 建構函式
   UnionFind(int n) {
         count = n;
         id = new int[n];
 // 初始化, 每一個id[i]指向自己, 沒有合併的元素,每一個數都是一個集合
        for (int i = 0; i < n; i++)
           id[i] = i;
 }

 // 解構函式
  ~UnionFind() {
        delete[] id;
  }
  1. find的實現:(查詢元素所在的集合編號,直接返回陣列值,O(1) 的時間複雜度。)

    // 查詢過程, 查詢元素p所對應的集合編號
    int find(int p) {
         assert(p >= 0 && p < count);
         return id[p];
    }
  2. isConnected的實現:

    // 檢視元素p和元素q是否所屬一個集合
    // O(1)複雜度
    bool isConnected(int p, int q) {
           return find(p) == find(q);
    }
  3. union的實現:(合併元素 p 和元素 q 所屬的集合, 合併過程需要遍歷一遍所有元素, 再將兩個元素的所屬集合編號合併,這個過程是 O(n) 複雜度。)

    // 合併元素p和元素q所屬的集合
    // O(n) 複雜度
    void unionElements(int p, int q) {   //union在c++中是一個關鍵字,所以這裡用 unionElements
    
         int pID = find(p);
         int qID = find(q);
    
         if (pID == qID)
              return;
    
    // 合併過程需要遍歷一遍所有元素, 將兩個元素的所屬集合編號合併
         for (int i = 0; i < count; i++)
                 if (id[i] == pID)
                     id[i] = qID;
    }
本作品採用《CC 協議》,轉載必須註明作者和本文連結

相關文章