1. 程式人生 > 其它 >【並查集】AcWing836. 合併集合 —— 並查集入門

【並查集】AcWing836. 合併集合 —— 並查集入門

並查集

處理問題

1.將兩個集合合併
2.詢問兩個集合是否在一個集合當中

暴力思維

1.詢問兩個元素是否在一個集合

belong[x] = a; //元素x在a集合
if(belong[x] == belong[y]) //判斷a與b是否在一個集合,複雜度O(1)

2.合併集合

//將a,b集合合併為c集合,時間複雜度O(n)
if(belong[x] == a) belong[x] = c;
if(belong[y] == b) belong[y] = c;

並查集可以在近乎O(1)複雜度完成以上兩個操作。

基本原理

每個集合用一棵樹來表示,樹根的編號就是整個集合的編號。每個結點儲存它的父節點,p[x]表示x的父節點。
問題1:如何判斷樹根:if(p[x] == x)
問題2:如何求x的集合編號:while(p[x] != x) x = p[x];
問題3:如何合併兩個集合:p[x]是x的集合編號,p[y]是y的集合編號,合併——p[x]=y

優化:路徑壓縮

當我們進行一次查詢後,將路途上的所有結點的父親改為根節點,下次查詢即可以O(1)查的根節點

AcWing836. 合併集合

題解

#include <iostream>
#include <cstdio>

using namespace std;

const int N = 1e5 + 10;

string s;
int p[N], n, m, x, y;

int find(int x) //查詢集合與路徑壓縮
{
    if(p[x] != x) return p[x] = find(p[x]);
    return x;
}

int main()
{
    scanf("%d%d",&n, &m);
    for(int i = 1; i <= n; ++i) p[i] = i;
    
    while(m -- )
    {
        cin >> s;
        scanf("%d%d", &x, &y);
        if(s == "M")    p[find(x)] = find(y); //一定要用父節點合併這樣才能保證根節點唯一
        else printf("%s\n", find(x) == find(y) ? "Yes":"No");
    }
    
    return 0;
}