什么是并查集:
并查集其实就是一种关系的链接,通过某一个数追根溯源到它最终关联的数。
在这里举例,有三个元素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]的时候就能够直接输出了,因为你之前已经递归转移完毕了。这就是递归压缩。