高级数据结构之并查集

并查集简介:

  • 维护同一种情况的集合。

一、基础版并查集:

基本步骤:初始化、不断合并的过程中查找、统计集合。
树状图:
在这里插入图片描述

#include <bits/stdc++.h>
using namespace std;
const int maxn = 1005;
int s[maxn];

void init_set(int n)//初始化
{
    for (int i = 1; i <= n; i++)
        s[i] = i;
}
int find_set(int x)//查找根节点
{
    return x==s[x]?x:find_set(s[x]);
}
void union_set(int x,int y)//合并集合
{
    x=find_set(s[x]);
    y=find_set(s[y]);
    if(x!=y)
        s[x]=s[y];
}
int main()
{
    ios::sync_with_stdio(false);
    int n, m, x, y;
    while (cin >> n >> m && n)
    {
        int ans = 0;
        init_set(n);
        for (int i = 1; i <= m; i++)
        {
            cin >> x >> y;
            union_set(x,y);
        }
        for(int i=1;i<=n;i++)//有几种不同的根
            if(i==s[i])
                ans++;
        cout << ans << endl;
    }
    return 0;
}

由于查找find_set()、合并union_set()正如上图一样,搜索深度为O(n),n为树的高度。可以分别对查找与合并进行优化(树的高度)。


二、合并的优化

步骤:

  1. 在合并x,y的时候先搜索到他们的根节点,并合并这两个根节点。
  2. 根据这两个根节点的高度不同,把高度小的加到高度大的集合上
    利用数组int height[]来记录高度
const int maxn = 1005;
int s[maxn], height[maxn];
void init_set()
{
    for (int i = 1; i <= maxn; i++)
    {
        s[i] = i;
        height[i] = 1; //每棵树的高度
    }
}

void union_set(int x, int y)
{
    x = find_set(x);
    y = find_set(y);
    if (height[x] == height[y])
    {
        height[x]++; //将y合并到x上,树的高度+1;
        s[y] = x;
    }
    else
    {
        if (height[x] > height[y]) //将较小的树加到较大的树上
            s[y] = x;
        else
            s[x] = y;
    }
}

三、查询的优化–路径压缩

树状图:

在这里插入图片描述

步骤:
搜索的过程中,改变根节点。
优点:
优化了下一次的查询,也优化了合并。

1.递归版

int find_set(int x)
{
    if (x != s[x])
        s[x] = find_set(s[x]);//路径压缩
    return s[x];
}

2.非递归版:(数据过大,爆栈时应用)

int find_set(int x)
{
    int r = x;
    while (s[r] != x) //找根节点,并赋值给r
        r = s[r];
    int i = x, tmp;
    while (i != r)
    {
        tmp = s[i];
        s[i] = r; //将路径上的元素改为根节点
        i = tmp;
    }
}

利用并查集维护两个对立集合

发布了33 篇原创文章 · 获赞 33 · 访问量 6163

猜你喜欢

转载自blog.csdn.net/acm_durante/article/details/104091047
今日推荐