图基础之不相交集(并查集)

主要用于避免遍历的时候做无用功。

// 用于处理不相交集合的合并问题。

// 经典应用有:
// –连通子图
// –最小生成树Kruskal算法
// –最近公共祖先
// O(n)
#include <bits/stdc++.h>   // 万能库,测试可用,生产不可用
using namespace std;
const int N = 100;
int s[N];

int height[N];

// 初始化
void init_set()
{
    for (int i = 1; i <= N; i++)
    {
        s[i] = i;
        height[i] = 0; // 树的高度
    }
}

// 查找
int find_set(int x)
{
    int r = x;
    while (s[r] != r)
        r = s[r]; // 找到根结点
    int i = x, j;
    while (i != r)
    {
        j = s[i]; // 用临时变量j记录
        s[i] = r; // 把路径上元素的集改为根结点
        i = j;
    }
    return r;
}

// 合并
void merge_set(int x, int y)
{
    x = find_set(x);
    y = find_set(y);
    if (height[x] == height[y])
    {
        height[x] = height[x] + 1; // 合并,树的高度加一
        s[y] = x;
    }
    else
    { // 把矮树并到高树上,高树的高度保持不变
        if (height[x] < height[y])
            s[x] = y;
        else
            s[y] = x;
    }
}

int main()
{
    // m 次合并,x y代表一次合并的两个点
    // n 表示初始点的总数
    int t, n, m, x, y;
    // t个测试
    cout << "输入测试次数:" << endl;
    cin >> t;
    while (t--)
    {
        cout << "输入点总数和合并次数:" << endl;
        cin >> n >> m;
        init_set();
        for (int i = 1; i <= m; i++)
        {
            cout << "请输入第" << i << "对合并数:" << endl;
            cin >> x >> y;
            merge_set(x, y); // 合并x和y
        }
        int ans = 0;
        for (int i = 1; i <= n; i++) // 统计有多少个集
            if (s[i] == i)
                ans++;
        cout << "共有" << ans << "个唯一集合" << endl;
    }
    return 0;
}

[lightdb@lightdb-dev graph_basic]$ ./UnionFind-1-worst
输入测试次数:
2
输入点总数和合并次数:
4 2
请输入第1对合并数:
1 2
请输入第2对合并数:
3 4
共有2个唯一集合
输入点总数和合并次数:
5 3
请输入第1对合并数:
1 2
请输入第2对合并数:
1 3
请输入第3对合并数:
1 1
共有3个唯一集合

posted @ 2024-01-02 22:05  zhjh256  阅读(8)  评论(0编辑  收藏  举报