算法基础——合并集合

原题连接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;
}

🤪没有啦!感谢阅读!若是以为写的还不错的话,记得点一下右边的大拇指嗷!~

相关文章
相关标签/搜索