《算法竞赛从入门到国奖》算法基础:数据结构-并查集

💡Yupureki:个人主页

✨个人专栏:《C++》 《算法》《Linux系统编程》《高并发内存池》


🌸Yupureki🌸的简介:


目录

[1. 并查集前言](#1. 并查集前言)

[1.1 并查集的初始化](#1.1 并查集的初始化)

[1.2 查询操作](#1.2 查询操作)

[1.3 合并操作](#1.3 合并操作)

[1.4 判断操作](#1.4 判断操作)

[2. 【模板】并查集](#2. 【模板】并查集)

算法原理

代码实现

[3. 亲戚](#3. 亲戚)

算法原理

代码实现

[4. [USACO10OCT] Lake Counting S](#4. [USACO10OCT] Lake Counting S)

算法原理

代码实现

[5. 程序自动分析](#5. 程序自动分析)

算法原理

代码实现


1. 并查集前言

在有些问题中,我们需要维护若干个集合,并且基于这些集合要频繁执行下面的操作:

  • 查询操作:查找元素属于哪一个集合。一般会在每个集合中选取一个元素作为代表,查询的是这个集合中的代表元素;
  • 合并操作:将元素所在的集合与元素y所在的集合合并成一个集合;(注意,合并的是元素所在的集合,不是这两个元素!)
  • ·判断操作:判断元素和y是否在同一个集合

因此这就引出了一个新的数据结构:并查集。并查集的本质是一课树,这颗树的上的所有节点具有相同的某种属性,因此串在一起,表示一个集合。

1.1 并查集的初始化

我们通常用数组来表示并查集,这个数组中的下标表示节点序号 ,值表示他的父节点

初始时,其父节点就是自己

cpp 复制代码
const int N = 1e6 + 10;
int n;
int fa[N]; // 双亲表⽰法所需的数组
// 初始化并查集
void init()
{
    for(int i = 1; i <= n; i++) fa[i] = i;
}

1.2 查询操作

并查集中一颗树中有很多个节点,为了方便表示一棵树,我们通常用一颗树的根节点来代表这课树。因此如果两个节点他们的根节点相同 ,代表他们在同一个并查集

根节点的父节点等于其自身,因此遇到父节点等于其自身时即可停下

cpp 复制代码
// 查询操作
int find(int x)
{
    return fa[x] == x ? x : find(fa[x]);
}

例如在上图中,从H和G不停地向上找,直到找到根节点,此时发现他们的根节点相同,因此他们在同一个并查集中

1.3 合并操作

将x元素所在的集合与元素y所在的集合合并成一个集合:

本质其实就是让y的根节点成为x的根节点的父节点,这样顺带x和y都被连在了一起

cpp 复制代码
// 合并操作
void un(int x, int y) // 注意,函数名字不能⽤ union,因为它是 C++ 的关键字
{
    int fx = find(x);
    int fy = find(y);
    fa[fx] = fy;
}

1.4 判断操作

判断元素c和元素y是否在同一集合:

看看两者所在树的根节点是否相同。

cpp 复制代码
bool issame(int x, int y)
{
    return find(x) == find(y);
}

2. 【模板】并查集

题目链接:

P3367 【模板】并查集 - 洛谷

算法原理

并查集的模板题

代码实现

cpp 复制代码
#include <iostream>
#include <vector>
using namespace std;

vector<int> v(1);

int find(int x)
{
    return v[x] == x?x:v[x] = find(v[x]);
}

void un(int x,int y)
{
    int rootx = find(x);
    int rooty = find(y);
    v[rootx] = rooty;
}

int main()
{
    int n,m;
    cin>>n>>m;
    for(int i = 1;i<=n;i++)
        v.push_back(i);
    while(m--)
    {
        int a,b,c;cin>>a>>b>>c;
        if(a == 1)
            un(b,c);
        else
        {
            if(find(b) == find(c))
               cout<<"Y"<<endl;
            else
                cout<<"N"<<endl;
        }
    }
    return 0;
}

3. 亲戚

题目链接:

P1551 亲戚 - 洛谷

算法原理

你与亲戚的亲戚之间也是亲戚

因此把所有具有亲戚相关的节点看作一个大家庭,相当于在并查集中串在一起

代码实现

cpp 复制代码
#include <iostream>
#include <vector>
using namespace std;

vector<int> father(1);

int find(int x)
{
    return father[x] == x ? x : father[x] = find(father[x]);
}

void un(int x, int y)
{
    int fx = find(x);
    int fy = find(y);
    father[fx] = fy;
}

int main()
{
    int n, m, p; cin >> n >> m >> p;
    for (int i = 1; i <= n; i++)
    {
        father.push_back(i);
    }
    for (int i = 0; i < m; i++)
    {
        int a, b; cin >> a >> b;
        un(a, b);
    }
    while (p--)
    {
        int a, b; cin >> a >> b;
        if (find(a) == find(b))
            cout << "Yes" << endl;
        else
            cout << "No" << endl;
    }
    return 0;
}

4. [USACO10OCT] Lake Counting S

题目链接:

P1596 [USACO10OCT] Lake Counting S - 洛谷

算法原理

bfs + 并查集

在bfs中,把相邻的水地用并查集串在一起,表示为一整个大池塘

代码实现

cpp 复制代码
#include <iostream>
#include <vector>
#include <queue>
using namespace std;

int n, m;
vector<vector<char>> v;
vector<int> father;
int dx[4] = { 1,1,1,0 };
int dy[4] = { -1,1,0,1 };

int find(int x)
{
    return father[x] == x ? x : father[x] = find(father[x]);
}

void un(int x, int y)
{
    int fx = find(x);
    int fy = find(y);
    father[fx] = fy;
}

void bfs(int x, int y)
{
    queue<pair<int, int>> q;
    q.push({ x,y });
    int pos = x * n + y;
    father[pos] = pos;
    while (q.size())
    {
        auto p = q.front();
        q.pop();
        int a = p.first;
        int b = p.second;
        for (int k = 0; k < 4; k++)
        {
            int i = a + dx[k];
            int j = b + dy[k];
            if (i < 0 || i >= n || j < 0 || j >= m || v[i][j] == '.')
                continue;
            un(i*n+j,a*n+b);
            q.push({ i,j });
        }
    }
}

int main()
{
    cin >> n >> m;
    v.resize(n);
    father.assign(n * m, -1);
    for (int i = 0; i < n; i++)
    {
        for (int j = 0; j < m; j++)
        {
            char ch; cin >> ch;
            v[i].push_back(ch);
        }
    }
    int ret = 0;
    for (int i = 0; i < n; i++)
    {
        for (int j = 0; j < m; j++)
        {
            if (v[i][j] == 'W' && father[i * n + j] == -1)
            {
                ret++;
                bfs(i, j);
            }
        }
    }
    cout << ret;
    return 0;
}

5. 程序自动分析

题目链接:

P1955 [NOI2015] 程序自动分析 - 洛谷

算法原理

先利用并查集维护所有相等的信息,然后遍历所有的不相等信息,判断一下是否合法。

因为数据范围的问题,需要先对所有的数离散化处理。

代码实现

cpp 复制代码
#include <iostream>
#include <vector>
#include <unordered_map>
#include <unordered_set>
#include <algorithm>
using namespace std;

vector<int> fa;

int find(int x)
{
    return fa[x] == x ? x : fa[x] = find(fa[x]);
}

void un(int x, int y)
{
    int fx = find(x);
    int fy = find(y);
    fa[fx] = fy;
}

int main()
{
    int n; cin >> n;
    while (n--)
    {
        fa.clear();
        vector<long long> v;
        unordered_map<long long, int> dis;
        vector<pair<long long, long long>> m;
        vector<pair<long long, long long>> check;
        int k; cin >> k;
        while (k--)
        {
            long long i, j, e; cin >> i >> j >> e;
            v.push_back(i);
            v.push_back(j);
            if (e == 1)  
                m.push_back({ i,j });
            else
                check.push_back({ i,j });
        }
        sort(v.begin(), v.end());
        v.erase(unique(v.begin(), v.end()),v.end());
        for (int i = 0; i < v.size(); i++)
            dis.insert({ v[i],i });
        for (int i = 0; i < dis.size();i++)
            fa.push_back(i);
        for (auto& it : m)
        {
            un(dis[it.first], dis[it.second]);
        }
        int r = 1;
        for (auto& it : check)
        {
            if (find(dis[it.first]) == find(dis[it.second]))
            {
                cout << "NO" << endl;
                r = 0;
                break;
            }
        }
        if (r)
            cout << "YES" << endl;

    }
    return 0;
}
相关推荐
DeepModel1 小时前
【概率分布】伯努利分布详解
算法·概率论
再一次等风来1 小时前
声源定位算法5----SRP-PHAT(2)
算法·信号处理·srp·声源定位·gcc-phat
Andy1 小时前
Cpp语法1
c++·c
CappuccinoRose2 小时前
MATLAB学习文档 - 汇总篇
学习·算法·matlab
艾莉丝努力练剑2 小时前
静态地址重定位与动态地址重定位:Linux操作系统的视角
java·linux·运维·服务器·c语言·开发语言·c++
菜鸟小九2 小时前
hot100(31-40)
java·算法
gfdhy2 小时前
【Linux】服务器网络与安全核心配置|静态IP+SSH加固+防火墙,公网服务器必学实操
linux·服务器·网络·tcp/ip·算法·安全·哈希算法
王老师青少年编程2 小时前
2025年3月GESP真题及题解(C++七级): 选择题和判断题(题解)
c++·真题·gesp·答案·csp·信奥赛·七级
Frostnova丶2 小时前
LeetCode 1888 使二进制字符串交替的最少翻转次数
算法·leetcode