算法基礎——合併集合

原題連接ios

題目:數組

一共有n個數,編號是1~n,最開始每一個數各自在一個集合中。優化

如今要進行m個操做,操做共有兩種:spa

「M a b」,將編號爲a和b的兩個數所在的集合合併,若是兩個數已經在同一個集合中,則忽略這個操做;
「Q a b」,詢問編號爲a和b的兩個數是否在同一個集合中;code

輸入格式ci

第一行輸入整數n和m。get

接下來m行,每行包含一個操做指令,指令爲「M a b」或「Q a b」中的一種。io

輸出格式stream

對於每一個詢問指令」Q a b」,都要輸出一個結果,若是a和b在同一集合內,則輸出「Yes」,不然輸出「No」。原理

每一個結果佔一行。

數據範圍

1 ≤ n, m ≤ 10^5

輸入樣例:

4 5
M 1 2
M 3 4
Q 1 2
Q 1 3
Q 3 4

輸出樣例:

Yes
No
Yes

首先,咱們要知道並查集能夠作哪些操做?

  1. 並查集能夠將兩個集合進行合併);

  2. 並查集能夠判斷兩個元素是否在同一個集合當中)。

基本原理: 咱們將每一個集合用一棵樹(不必定是二叉樹)來表示,這棵樹的樹根的編號就是整個集合的編號。而每個節點都存儲它本身自己的父節點,在這題中咱們用 p[x] 這麼一個數組來存儲每個節點(x)的父節點。

Q & A :

Q1 : 如何判斷該節點是否爲樹根節點?(如下的 p[] 數組均存儲的是節點的父節點

A1 :除了根節點以外的點,p [x] != x ,因此若是 p [x] = x 的話,那麼 x 必定就爲這棵樹的根節點。 對應代碼就是:

bool is_root(){
      if(p[x] == x) return true;
      else return false;
}

Q2 :如何求 x 的集合編號?

A2 :當 p [x] 存儲的節點不等於 x 的時候,那麼此時的 x 必定不是根節點,這時咱們就繼續往上面的父節點找,直到找到根節點爲止,對應代碼就是這樣的:

int findRoot(){
      while(p[x] != x) x = p[x];
      return x;
}

Q3 : 如何合併兩個集合?

A3 :假設 p[x] 爲 x 的集合 1 的編號, p[y] 是 y 的集合 2 編號。此時咱們只須要讓集合 1 的根節點連上集合 2 的根節點便可。對應到代碼大概講長這樣: p[集合 1 根節點] = 集合 2 根節點

若是要實現以上這些步驟,其實時間複雜度仍是挺高的,這時咱們就要引入一個新的話題,如何將並查集問題進行優化呢?

並查集的優化:路徑壓縮

如何解釋路徑壓縮?

我的理解: 假設,當 x 所在的這個節點,經過千辛萬苦終於找到了它的根節點,那麼咱們就讓 x 這個節點在找根節點時通過的全部的父節點都會直接指向根節點上

說了這麼多,下面就正式開始進入敲代碼環節吧!

首先,咱們須要把 p[] 這個存儲每一個節點的父節點的數組給初始化了,咱們讓每一個節點的父節點都指向本身也就是:

for(int i = 0; i < n; i++) p[i] = i;

第二步,就是開始寫咱們的並查集最最核心的操做了——尋找根節點,同時,咱們須要在尋找根節點的時候,加上並查集的路徑壓縮,對並查集作優化。

int findRoot(int x){
      if(p[x] != x) p[x] = find(p[x]);      //若是當前 x 不是根節點,咱們就讓他的父節點去找根節點,這就在尋找根節點的同時作了路徑壓縮優化了(妙啊~~~~~)
      return p[x];
}

最後,附上完整AC代碼:

#include <iostream>
#include <cstdio>

using namespace std;
const int N = 100010;

int p[N];
int n, m;

int findRoot(int x){
    if(p[x] != x) p[x] = findRoot(p[x]);
    return p[x];
}

int main(){
    cin >> n >> m;
    for(int i = 1; i <= n; i++) p[i] = i;
    
    while(m --){
        char op[2];
        int a, b;
        scanf("%s%d%d", op, &a, &b);
        if(op[0] == 'M') p[findRoot(a)] = findRoot(b);
        else{
            if(findRoot(a) == findRoot(b)) cout << "Yes" << endl;
            else cout << "No" << endl;
        }
    }
    
    return 0;
}

🤪沒有啦!感謝閱讀!若是以爲寫的還不錯的話,記得點一下右邊的大拇指嗷!~

相關文章
相關標籤/搜索