蓝桥杯·全国青少年信息素养大赛·电子学会C/C++考级:并查集连通块与朋友圈问题解析

一、引言:为什么一定要会并查集

无论你是冲 蓝桥杯 (第十七届省赛已在 2026 年 4 月落幕、第十八届报名季随之开启),还是在准备 全国青少年信息素养大赛 算法应用赛项(复赛大纲强调字符串、列表、函数与递归等基础能力),又或是正在报名 中国电子学会 2026 年 12 月软件编程考级(C/C++ 一级至十级、Python 一级至六级,报名 9 月 23 日---12 月 2 日,12 月 12 日开考)------「集合合并与连通性判断」都是绕不开的一类题。

这类题在真题里常化身成:

  • 班里有几「朋友圈」(连通块计数);
  • 两个人是不是同一个社团的(连通性查询);
  • 网络里加一条线会不会成环(判环);
  • 若干村庄最少修几段路全连通(最小生成树的前置思路)。

它们的底层数据结构,就是 并查集(Disjoint Set Union,DSU,也叫不相交集合)。本文用一个原创题带你吃透它,并给出 C++ 与 Python 双版本,再延伸到网格、判环与种类并查集。


二、题目:校园朋友圈统计

【原创题】校园朋友圈

某班有 n 名同学(编号 1 ~ n)。已知 m 对「朋友关系」,朋友关系双向传递:如果 A 和 B 是朋友,B 和 C 是朋友,那么 A、B、C 就属于同一个朋友圈。

请完成两件事:

  1. 输出全班最终形成的朋友圈数量 ;

  2. 支持 q 次询问:编号为 a 和 b 的两名同学是否在同一朋友圈?

输入示例

复制代码
6 4
1 2
2 3
4 5
2 5
3
1 4
1 6
5 6

输出示例

复制代码
朋友圈数量 = 2
同朋友圈
不同朋友圈
不同朋友圈

样例解释 :1-2-3 通过 2-5 与 4-5 连通,于是 {1,2,3,4,5} 是一个朋友圈;6 单独一个,共 2 个朋友圈。1 和 4 同圈,1 和 6 不同圈,5 和 6 也不同圈。


三、核心考点拆解

考点 说明
初始化 每个人最开始是自成一个集合,parent[i] = i,朋友圈数 = n
find 查找根 顺着 parent 往上找,直到 parent[x] == x;用路径压缩把沿途节点直接挂在根上,加速后续查询
unite 合并 找到两点的根,若不同根则把一棵树挂到另一棵下面,并让朋友圈数减一
same 连通判断 两人同圈 ⇔ 它们的根相同(find(a) == find(b))
按大小合并 把「小树」挂到「大树」下,保证树高不超过 O(log n),避免退化成链

关键直觉 :每成功合并一次,集合数就减一;一开始有 n 个孤立点,最后剩下的集合数就是朋友圈数量。


四、解法与代码实现

思路

  1. 初始化 n 个集合,comps = n。
  2. 对每条朋友关系 (u, v):若 u、v 当前不在同一集合,就合并,并把 comps 减一。
  3. 所有关系处理完后,comps 即朋友圈数量。
  4. 询问直接比较两人根是否相同。

C++ 版本

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

struct DSU {
    vector<int> parent, sz;
    int comps;
    DSU(int n) : parent(n + 1), sz(n + 1, 1), comps(n) {
        for (int i = 1; i <= n; i++) parent[i] = i;
    }
    // 查找根 + 路径压缩(迭代写法,避免递归爆栈)
    int find(int x) {
        while (parent[x] != x) {
            parent[x] = parent[parent[x]]; // 路径减半压缩
            x = parent[x];
        }
        return x;
    }
    // 合并两集合,返回是否真正发生了合并
    bool unite(int a, int b) {
        a = find(a); b = find(b);
        if (a == b) return false;          // 已在同一集合
        if (sz[a] < sz[b]) swap(a, b);     // 按大小合并
        parent[b] = a;
        sz[a] += sz[b];
        comps--;
        return true;
    }
    bool same(int a, int b) { return find(a) == find(b); }
};

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int n, m;
    cin >> n >> m;
    DSU dsu(n);
    for (int i = 0; i < m; i++) {
        int u, v;
        cin >> u >> v;
        dsu.unite(u, v);
    }
    cout << "朋友圈数量 = " << dsu.comps << "\n";
    int q; cin >> q;
    while (q--) {
        int a, b; cin >> a >> b;
        cout << (dsu.same(a, b) ? "同朋友圈" : "不同朋友圈") << "\n";
    }
    return 0;
}

Python 版本

python 复制代码
class DSU:
    def __init__(self, n):
        self.parent = list(range(n + 1))
        self.sz = [1] * (n + 1)
        self.comps = n

    def find(self, x):
        while self.parent[x] != x:
            self.parent[x] = self.parent[self.parent[x]]
            x = self.parent[x]
        return x

    def unite(self, a, b):
        a, b = self.find(a), self.find(b)
        if a == b:
            return False
        if self.sz[a] < self.sz[b]:
            a, b = b, a
        self.parent[b] = a
        self.sz[a] += self.sz[b]
        self.comps -= 1
        return True

    def same(self, a, b):
        return self.find(a) == self.find(b)


def solve():
    n, m = map(int, input().split())
    dsu = DSU(n)
    for _ in range(m):
        u, v = map(int, input().split())
        dsu.unite(u, v)
    print("朋友圈数量 =", dsu.comps)
    q = int(input())
    for _ in range(q):
        a, b = map(int, input().split())
        print("同朋友圈" if dsu.same(a, b) else "不同朋友圈")


if __name__ == "__main__":
    solve()

五、时间与空间复杂度

  • 时间复杂度 :每次 find 经过路径压缩 + 按大小合并后,单次操作近似 O(α(n)),α 是反阿克曼函数(增长极慢,n=10^9 时也基本不超过 4)。整体约 O((n + m) · α(n)) ≈ O(n + m)。
  • 空间复杂度 :两个数组 parent、sz,O(n)。

对比朴素做法(每次合并暴力改标记 O(n)),并查集把「集合合并」从 O(n²) 压到接近线性,是蓝桥杯大赛里处理「动态连通」的标配。


六、进阶拓展

进阶 1:网格岛屿数量(二维并查集)

把 r 行 c 列的格子 (r, c) 映射成一维编号 r * cols + c,其余逻辑与一维完全相同:

cpp 复制代码
int id(int r, int c, int cols) { return r * cols + c; }
// 对每个陆地格子,与上下左右四个邻居(若都是陆地)unite 即可

这就是蓝桥杯里常见的「岛屿数量 / 省份数量」题的并查集解法(也可用 DFS/BFS Flood Fill,思路相通)。

进阶 2:并查集判环

在无向图中加边 (u, v) 前先判断:if (dsu.same(u, v)) 说明两点已连通,再加这条边必成环;否则 unite。常用于「最少删几条边不成环」「K 度约束生成树」等。

进阶 3:按秩(rank)合并

本文用「按大小」合并;也可以记录树高 rank,把矮树挂到高树下,效果等价,都能保证树高 O(log n)。

进阶 4:与种类 / 带权并查集对照

当关系不止「同/不同」,而是「A 吃 B、B 吃 C」这类带相对关系 时,就要在 find 时同步维护偏移量、在 unite 时列方程求新根权值------这就是「种类并查集 / 带权并查集」(如经典的食物链、阵营冲突模型)。它是在本篇「基础连通」之上加了一层关系编码,建议先吃透本文再上难度。

进阶 5:最小生成树(Kruskal)

Kruskal 算法每选一条最小权边,都要用并查集判断「两端是否已连通」来避免成环------所以并查集也是最小生成树的前置积木。


七、易错点清单

  1. 忘记初始化 :parent[i] 必须初始为 i,否则所有查找都会错。
  2. 路径压缩写错 :应写成 parent[x] = find(parent[x])(或迭代版 parent[x] = parent[parent[x]]),返回的是根,而不是 find(x) 后再赋值给无关变量。
  3. 合并前没先 find:直接拿原始编号去挂,会把树接错;一定要先取各自的根。
  4. comps 乱减 :只有「真正发生合并」时才 comps--,两人本就同圈时不能减。
  5. 多组测试未重置 :有多组数据时要重新 new 一个并查集,别复用旧数组。
  6. 下标从 0 还是 1 起 :题目编号若从 1 开始,数组开 n+1 更省心;若是 0 起,注意映射不要越界。

八、小结与互动

并查集三句话记牢:初始化各自为营 → 查根用路径压缩 → 合并按大小(或秩)挂、合并成功才计数。它能把「连通块数量、是否连通、是否成环、最小生成树」一串题一次性串起来,是蓝桥杯、信息素养大赛算法应用、电子学会 C/C++ 与 Python 考级的高级必备结构。

今日小互动:如果把题目改成「每次询问两人是否同圈」改成「动态加朋友关系、随时问两人是否同圈」,你的并查集需要改哪一部分?欢迎在评论区写下你的思路,或贴出你用其他语言(如 Java)的实现版本,一起交流~

如果觉得有帮助,点赞 + 收藏 + 关注,后续还会更新「差分」「前缀和」「LCA」等蓝桥杯高频考点专题。


📚 免费少儿编程资料(夸克网盘领取)

以下资料来自夸克网盘分享,点击链接可直接保存;若需在 App 内打开,也可复制下方明文链接:

  1. 全国青少年信息素养大赛复赛集训题目Python&C++.docx
    https://pan.quark.cn/s/93995d3cb150
  2. 2024信息素养-智能算法应用挑战赛-复赛初中组题目7月7日.pdf
    https://pan.quark.cn/s/da97b5dbf75d
  3. Python背记手册.pdf
    https://pan.quark.cn/s/7568ae9ca92b
  4. Python课程
    https://pan.quark.cn/s/a94bf02d00c6
  5. 2024信息素养大赛图形化复赛集训题答案3-9
    https://pan.quark.cn/s/6ccab7ec3cbc
  6. 2025年03月份电子学会考级真题
    https://pan.quark.cn/s/4403c4228912
  7. 2025全国青少年信息素养大赛赛项说明
    https://pan.quark.cn/s/d9d0df4a9f29
  8. 青少儿信息素养大赛编程资料
    https://pan.quark.cn/s/4ab6bd83be8a

资料持续更新,关注获取最新分享。

相关推荐
wyhwust1 小时前
codeblocks程序调试
c++
Tisfy1 小时前
LeetCode 2913.子数组不同元素数目的平方和 I:第一题先常数很大的O(n^2)
算法·leetcode·题解·模拟·暴力
外收内放1 小时前
数据分析(数据排序)
python·学习·数据分析
罗湖老棍子2 小时前
[USACO05NOV] Asteroids G(洛谷-P7368)
算法·图论·二分图匹配·最大匹配·最小顶点覆盖
Pniubi2 小时前
agent处理封号怎么解决误封问题
开发语言·python
AI技术新视界2 小时前
Harness^2:递归自进化智能体Harnessing框架
人工智能·深度学习·算法·harness·rsi
这个DBA有点耶2 小时前
MySQL B-tree索引并发控制优化:写密集场景的锁瓶颈与内核级解法
java·数据库·程序人生·算法·database·dba
爱读书的小胖2 小时前
Deaf Grandma:C 语言三版代码分析
算法
傲世仙尊2 小时前
运算符重载-operator函数与五个不能重载的符号
c++