并查集

· 浏览次数 : 5

小编点评

```c++ #include <iostream>using namespace std;const int N = 100010;int n, m;int p[N];int find(int x) { if (p[x] != x) p[x] = find(p[x]); return p[x];}int main() { cin >> n >> m; for (int i = 1; i <= n; ++i) p[i] = i; while (m--) { string op; int x, y; cin >> op >> x >> y; x = find(x), y = find(y); if (op == "M") p[x] = y; else { if (x == y) puts(\"Yes\"); else puts(\"No\"); } } return 0;} ```

正文

例题

一共有 \(n\) 个数,编号是 \(1 \sim n\),最开始每个数各自在一个集合中。

现在要进行 \(m\) 个操作,操作共有两种:

  1. M a b,将编号为 \(a\)\(b\) 的两个数所在的集合合并,如果两个数已经在同一个集合中,则忽略这个操作;
  2. Q a b,询问编号为 \(a\)\(b\) 的两个数是否在同一个集合中;

输入格式

第一行输入整数 \(n\)\(m\)

接下来 \(m\) 行,每行包含一个操作指令,指令为 M a bQ a b 中的一种。

输出格式

对于每个询问指令 Q a b,都要输出一个结果,如果 \(a\)\(b\) 在同一集合内,则输出 Yes,否则输出 No

每个结果占一行。

数据范围

\(1 \le n,m \le 10^5\)

输入样例:

4 5
M 1 2
M 3 4
Q 1 2
Q 1 3
Q 3 4

输出样例:

No
Yes

什么是并查集

并查集常用于快速计算森林中有多少棵树。
并指合并操作,查指递归追查祖先,集指处理对象是集合。

如何实现并查集

初始化并查集

用一个一维数组 \(p\) 模拟并查集。开始时每个集合都是以自己为祖先
即:

for (int i = 1; i <= n; ++i)
    p[i] = i; // n为结点的个数(即数的个数)

合并操作

我们需要先实现一个函数:查找当前节点的祖先节点,在寻找祖先节点的过程中,使用类似记忆化搜索的方法同步更新父节点,这就是一种叫做路径压缩优化的技巧,可以有效减少追查深度。
代码:

int find(int x) { // 查找x的祖先节点(路径压缩)
    if (p[x] != x)
        p[x] = find(p[x]); // 祖先节点的父节点不是自己时更新父节点
    return p[x];
}

合并时,将两个集合的祖宗节点换掉就可以了

p[find(x)] = find(y);

查询操作

这个直接判断两个节点的祖宗节点是不是同一个节点就可以了,代码:

if (find(x) == find(y))
    puts("Yes");
else
    puts("No");

例题实现代码

#include <iostream>
using namespace std;
const int N = 100010;
int n, m;
int p[N];
int find(int x) {
    if (p[x] != x)
        p[x] = find(p[x]);
    return p[x];
}
int main() {
    cin >> n >> m;
    for (int i = 1; i <= n; ++i)
        p[i] = i;
    while (m--) {
        string op;
        int x, y;
        cin >> op >> x >> y;
        x = find(x), y = find(y);
        if (op == "M")
            p[x] = y;
        else {
            if (x == y)
                puts("Yes");
            else
                puts("No");
        }
    }
    return 0;
}

与并查集相似的内容:

并查集

例题 一共有 \(n\) 个数,编号是 \(1 \sim n\),最开始每个数各自在一个集合中。 现在要进行 \(m\) 个操作,操作共有两种: M a b,将编号为 \(a\) 和 \(b\) 的两个数所在的集合合并,如果两个数已经在同一个集合中,则忽略这个操作; Q a b,询问编号为 \(a\

算法学习笔记(4): 并查集及其优化

# 并查集 并查集,Disjoint-Set,或者通俗一点,叫做MergeFind-Set,是一种可以动态维护若干个不重叠的集合,并支持集合之间的合并与查询的数据结构。 集体来说,并查集支持下列两个操作: - Find,查询元素所属集合 - Merge,将两个元素所属集合合并 一般来说,为了具体实现

如何使用并查集解决朋友圈问题?

本文已收录到 GitHub · AndroidFamily,有 Android 进阶知识体系,欢迎 Star。技术和职场问题,请关注公众号 [彭旭锐] 私信我提问。 前言 大家好,我是小彭。 今天分享到的是一种相对冷门的数据结构 —— 并查集。虽然冷门,但是它背后体现的算法思想却非常精妙,在处理特定

LeetCode952三部曲之一:解题思路和初级解法(137ms,超39%)

这是难度为Hard的一道题,涉及到素数筛选和并查集基本操作,请随本文一同理清楚思路

LeetCode952三部曲之二:小幅度优化(137ms -> 122ms,超39% -> 超51%)

这是难度为Hard的一道题,涉及到素数筛选和并查集基本操作,请随本文一同理清楚思路

二分图(例题)

https://www.cnblogs.com/kuangbiaopilihu/p/18184536 $\quad $ 这里不再介绍二分图的基础知识,只是一些例题的解释。 $\quad $ 当然,这道题可以用二分+并查集来解决。但这是二分图专辑,所以介绍一下二分图做法。 $\quad $ 首先如果两

算法学习笔记(30):Kruskal 重构树

Kruskal 重构树 这是一种用于处理与最大/最小边权相关的一个数据结构。 其与 kruskal 做最小生成树的过程是类似的,我们考虑其过程: 按边权排序,利用并查集维护连通性,进行合并。 如果我们在合并时,新建一个节点,其权值为当前处理的边的权值,并将合并的两个节点都连向新建的节点,那么就可以得

GaussDB(DWS)条件表达式函数返回错误结果集排查

摘要:条件表达式函数中出现结果集不一致问题,我们首先要考虑是否入参数据类型不一致导致出参不一致。 本文分享自华为云社区《GaussDB(DWS)条件表达式函数返回错误结果集排查》,作者:yd_211369925 。 (一)案例背景 客户使用greatest获取并返回参数列表中值最大的表达式的值,子查

.NET 7 中 LINQ 的疯狂性能提升

LINQ 是 Language INtegrated Query 单词的首字母缩写,翻译过来是语言集成查询。它为查询跨各种数据源和格式的数据提供了一致的模型,所以叫集成查询。由于这种查询并没有制造新的语言而只是在现有的语言基础上来实现,所以叫语言集成查询。语言集成查询 (LINQ) 是一系列直接将查

核对不同文件夹所含内容的差异并提取缺失内容:Python代码

本文介绍基于Python语言,以一个大文件夹作为标准,对另一个大文件夹所包含的子文件夹或文件加以查漏补缺,并将查漏补缺的结果输出的方法~