Disjoint
Set

Disjoint Set

並查集

Disjoint Set

  • 給你\(n\)個集合,每個集合中分別有一個元素
    編號為\(1\sim n\)
  • 你有兩種操作可以做:
    1. Same(x,y):
      問你編號x的元素和編號y的元素是否在同一個集合中
    2. Union(x, y):
      將編號x的元素和編號y的元素,他們所在的集合進行合併

老大是誰

  • 眾所周知,在某些道上有很鮮明的階級制度
  • 當兩個互不認識的小弟遇到了
    通常不會問對方是哪個組織
    而是問「你老大是誰」

你老大是誰

X!羅大哥我老大

最高領導

  • 報上名的老大通常是組織的最高領導者
  • 大家就會知道你是哪個組織的

老大

黑吃黑

  • 有的時候會發生黑吃黑
  • 基本上就是組織A把另一個組織B給併吞了

A老大

B老大

A老大

(原)B老大

Disjoint Set

模擬道上系統

3

1

2

4

1 2 3 4
3 1 3 3

parent array

誰是老大

  • 一開始所有人都是自己的老大
  • 透過find去問自己上司的老大是誰,一直問到某個人他的上司是自己,那他就是老大
int parent[1000005];
void init(int n){
    for(int i = 1; i <= n ++i)
        parent[i] = i;
}
int Find(int x){
    if(parent[x] == x)
        return x;
    return Find(parent[x]);
}

黑吃黑

bool Same(int x, int y){
    return Find(x)==Find(y);
}
void Union(int x, int y){
    parent[Find(x)] = Find(y);
}

時間複雜度

  • 由於所有操作都是由Find構成,只需要分析Find的時間
  • 但很可惜Find最差會是\(O(n)\)

優化1

啟發式合併

紀錄高度

  • 老大紀錄整棵樹的高度
  • Union時把小的接到大的下面

3

2

紀錄高度

  • 老大紀錄整棵樹的高度
  • Union時把小的接到大的下面

3

紀錄高度

  • 兩棵樹高度一樣時
  • Union後高度才會增加

2

2

3

紀錄高度

int parent[1000005];
int h[1000005];
void init(int n){
    for(int i = 1; i <= n ++i){
        parent[i] = i;
        h[i] = 1;
    }
}
int Find(int x); // 和之前一樣
void Union(int x, int y){
    x = Find(x);
    y = Find(y);
    if(h[y] < h[x])
        swap(x,y);
    parent[x] = y;
    if(h[x] == h[y])
        ++h[y];
}

時間複雜度

  • 兩棵樹高度一樣時
    Union後高度才會增加
  • 所以一顆高度為\(k\)的樹
    至少會有\(2^{k-1}\)個點
  • 所以樹高為\(O(\log n)\),同時也是Find的時間複雜度

紀錄Size

  • 每個老大紀錄整棵樹的節點數量(Size)
  • 能得到一樣的時間複雜度
  • 且Size比高度有用

2

3

5

紀錄Size

int parent[1000005];
int sz[1000005];
void init(int n){
    for(int i = 1; i <= n ++i){
        parent[i] = i;
        sz[i] = 1;
    }
}
int Find(int x); // 和之前一樣
void Union(int x, int y){
    x = Find(x);
    y = Find(y);
    if(sz[y] < sz[x])
        swap(x,y);
    parent[x] = y;
    sz[y] += sz[x];
}

優化2

路徑壓縮

路徑壓縮

  • Find的過程中
    走過的點他們的老大都是同一個人
  • 那就乾脆直接指過去

R

R

路徑壓縮

int parent[1000005];
int Find(int x){
    if(parent[x] == x)
        return x;
    parent[x] = Find(parent[x]);
    return parent[x];
}

時間複雜度

  • 假設Find總共執行\(f\)次
  • \(\Theta(n+f\cdot(1+\log_{2+f/n} n))\)
  • 平均每次也是\(O(\log n)\)
    一般比賽用這個就足夠了

優化3

兩個優化一起做

兩個優化

  • 優化1只會動到Union
  • 優化2只會動到Find
  • 兩個一起做會發生甚麼事?

Find複雜度

\(O(\alpha(n))\)

\(\alpha(n)\)是反阿克曼函數,基本上等於常數!

練習題

POJ 1182

延伸

啟發式合併

啟發式合併

  • 假設種共有\(n\)個元素
    分別被放在\(m\)個資料結構\(T_1,...T_m\)中
  • 這些資料結構可以做合併的操作
    \(T_i.union(T_j)\)表示將\(T_j\)中所有元素丟到\(T_i\)中
Made with Slides.com