代码随想录算法训练营 Day47 | 图论 part05

并查集(Union-Find)理论基础

1. 基本概念

并查集是一种用于处理集合合并与查询的数据结构,主要支持两种操作:

  • Find(查找):判断某个元素属于哪个集合(找到根节点)
  • Union(合并):将两个集合合并成一个集合

常用于解决:

连通性问题(是否在同一集合)

2. 核心思想

用一个数组 parent 表示每个节点的"父节点",形成一棵树结构:

  • 每个集合有一个根节点(代表元)
  • 判断两个元素是否属于同一集合:
    👉 看它们的根节点是否相同

3. 基本操作

(1)初始化
复制代码
for(int i = 0; i < n; i++){
  parent[i] = i; // 每个节点的父节点是自己
}
(2)查找(Find)
复制代码
int find(int x){
  if(parent[x] != x){
    parent[x] = find(parent[x]); // 路径压缩
  }
  return parent[x];
}

作用:

  • 找到集合的根节点
  • 顺便进行路径压缩(优化)
(3)合并(Union)
复制代码
void unite(int x, int y){
  int rootX = find(x);
  int rootY = find(y);

  if(rootX != rootY){
    parent[rootY] = rootX; // 合并
  }
}

4. 优化技巧

(1)路径压缩

find 过程中,把所有节点直接挂到根节点上:

复制代码
parent[x] = find(parent[x]);

作用:

  • 降低树高度
  • 加快后续查询
(2)按秩合并(或按大小合并)
复制代码
if(rank[rootX] < rank[rootY]){
  parent[rootX] = rootY;
} else {
  parent[rootY] = rootX;
}

作用:

  • 保持树尽量平衡
  • 避免退化成链表

5. 时间复杂度

在路径压缩 + 按秩合并优化下:

  • 单次操作复杂度:接近 O(1)
  • 精确为:O(α(n))(反阿克曼函数,极慢增长)

可以认为是常数时间

6. 适用场景

并查集常用于:

  • 连通性判断(两个点是否连通)
  • 图的连通分量统计
  • 最小生成树(Kruskal)
  • 岛屿问题(动态合并)
  • 网络连接问题

107. 寻找存在的路线

题目描述

给定一个包含 n 个节点的无向图中,节点编号从 1 到 n (含 1 和 n )。

你的任务是判断是否有一条从节点 source 出发到节点 destination 的路径存在。

输入描述

第一行包含两个正整数 N 和 M,N 代表节点的个数,M 代表边的个数。

后续 M 行,每行两个正整数 s 和 t,代表从节点 s 与节点 t 之间有一条边。

最后一行包含两个正整数,代表起始节点 source 和目标节点 destination。

输出描述

输出一个整数,代表是否存在从节点 source 到节点 destination 的路径。如果存在,输出 1;否则,输出 0。

输入示例

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

输出示例

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

int n, m;
// father[i] 表示节点 i 的父节点
vector<int> father(101, -1);
// 初始化:每个节点的父节点都是自己
void init() {
    for (int i = 1; i <= n; i++) {
        father[i] = i;
    }
}
// 查找根节点(带路径压缩)
int find(int u) {
    // 如果不是根节点,就递归查找,并压缩路径
    return u == father[u] ? u : father[u] = find(father[u]);
}
// 合并两个集合
void join(int u, int v) {
    u = find(u);
    v = find(v);

    // 如果已经在同一集合,不需要合并
    if (u == v) return;

    // 将 v 的根挂到 u 上
    father[v] = u;
}
// 判断两个节点是否属于同一集合
bool issame(int u, int v) {
    u = find(u);
    v = find(v);
    return u == v;
}

int main() {
    cin >> n >> m;
    // 初始化并查集
    init();
    // 读入边并进行合并
    for (int i = 0; i < m; i++) {
        int s, t;
        cin >> s >> t;
        join(s, t);
    }
    int start, end;
    cin >> start >> end;
    // 判断 start 和 end 是否连通
    cout << issame(start, end) << endl;
    return 0;
}

总结

1. 核心思路

用并查集维护连通关系:

  • join(s, t):表示 s 和 t 连通
  • 最后判断 startend 是否在同一集合

本质是:

判断两个节点是否连通

2. 关键操作
  • find(u):查找根节点(带路径压缩)
  • join(u, v):合并两个集合
  • issame(u, v):判断是否在同一集合
3. 优化点

路径压缩:

复制代码
father[u] = find(father[u]);

作用:

  • 降低树高度
  • 提高查询效率
4. 复杂度
  • 单次操作:接近 O(1)
  • 总体复杂度:O(n + m)
相关推荐
猿长大人2 小时前
算法 | 轮廓提取随笔 —— 关于像素、阈值和直觉的碎碎念
图像处理·算法
啦啦啦_99992 小时前
1. 线性回归之 向量&矩阵
算法·矩阵·线性回归
DolphinDB智臾科技2 小时前
DolphinDB 走进东南大学 | 新型电力系统高频数据处理与算法落地实战
算法
Zzzzmo_2 小时前
前缀和算法
算法·前缀和
睡觉就不困鸭2 小时前
第十八天 有效的括号
数据结构·算法
_日拱一卒2 小时前
LeetCode:148排序链表
算法·leetcode·链表
IpdataCloud2 小时前
IP查询工具的准确率怎么评估?一份可上生产的选型与验收指南
网络·人工智能·算法
生信研究猿2 小时前
leetcode 78.子集
算法·leetcode·深度优先
sycmancia3 小时前
Qt——文本编辑器中的功能交互
qt·算法