并查集基础 -> P3367 【模板】并查集

什么是并查集:

并查集其实就是一种关系的链接,通过某一个数追根溯源到它最终关联的数。

在这里举例,有三个元素1,2,3。我们使用一个数组a,使得a[1]=1,a[2]=2,a[3]=3。(初始时,每一个数的初始位置都等于其本身,即只和自己有关联,这是所有并查集的特性)。当我们使得元素1和2合并为一个集合,那么我们可以使得1指向2,即a[1]=2=a[2]。这样就使得其关联了起来,然后当我们需要使得1和3合并或者2和3合并时。就使他们都指向3,即a[1]=3=a[3],a[2]=3=a[3],a[3]=3=a[3]。这种操作我们通过自己写的函数find和merge来实现。其中find用来查找这个数的源头是谁,merge用来链接。具体操作可以看此题操作。

初学并查集的题目:

#include<math.h>
#include<algorithm>
#include<time.h>
#include<stdlib.h>
#include<iostream>
#include<string.h>
#include<sstream>
#include<map>
#include<list>
#include<string>
#include<queue>
#include<set>
#include<vector>
#include<stack>
#include<limits>
#define re register
#define iosgo() std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
#define run(i,n) for (int i = 1; i <= n; i++)
#define cin std::cin
#define cout std::cout
#define ll long long
#define endl "\n"
using namespace std;
typedef pair<int, int>pll;
const int N = 2e6 + 10;
pll c[N];
int x[N], y[N], dp[N], ss[N];
int gcd(int a, int b)
{
    return b ? gcd(b, a % b) : a;
}
int find(int v)
{
    if (x[v] == v)return v;
    else return x[v] = find(x[v]);//在这里用了递归压缩,稍后解释
}
void merge(int u, int v)
{
    int a = find(u);
    int b = find(v);
    if (a != b)
    {
        x[a] = b;
    }
}
signed main()
{
    int n, m; cin >> n >> m;
    run(i, n)x[i] = i;
    run(i, m)
    {
        int a, b, c;
        cin >> a >> b >> c;
        if (a == 1)
        {
            merge(b, c);
        }
        else
        {
            if (find(b) == find(c))cout << "Y\n";
            else cout << "N\n";
        }
    }
}

递归压缩:

上文中使用了如下非常高级的代码:

int find(int v)
{
    if (x[v] == v)return v;
    else return x[v] = find(x[v]);
}

看不懂没关系,我们可以先看看它的低级写法

int find(int v)
{
    if (x[v] == v)return v;//找着了返回
    int t = x[v]; 
    find(x[v]);//没找着递归接着找 比如a[2]=3=a[3],我传进来就是a[2],此时a[2]!=2,所以我就去找3。
}

假如有100个数,从1到100,然后1->2->3····->100这样子链接起来。那我要查找100次a[1],是不是得作死的递归很多次。如果数据量更大,链接的更多那咋办。递归压缩。

原始状态下我们是一个一个链接,而这里的递归压缩则是使得该数直接链接向最终对应的位置。但是请注意,这也是很多初学者不明白递归压缩怎么达到压缩的一个点:

在第一次链接的时候我们仍然是通过不断的递归使得其连接,也就是说第一次让你输出a[1]到底链接向谁的时候你仍然需要递归,但是当你再次需要输出a[1]的时候就能够直接输出了,因为你之前已经递归转移完毕了。这就是递归压缩。

猜你喜欢

转载自blog.csdn.net/YZcheng_plus/article/details/129464982