代码随想录算法训练营 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 小时前
如何诊断 Prompt 模板导致的效果下降?
人工智能·算法·prompt
OPEN-F3 小时前
C++11/14新特性精讲:移动语义与智能指针实战
开发语言·c++·算法
lisin-lee-cooper4 小时前
【leetcode658】有序数组找出k个最接近x的数
java·数据结构·算法
sunburn-4 小时前
Java堆(Heap)详解与实战教学
java·开发语言·数据结构·ide·算法
智碳能碳管理平台5 小时前
工业能耗台账标准化:能碳管理系统的数据口径怎么设计
算法·能碳管理系统·智碳能碳管理平台·企业能碳管理系统·碳排放核算软件·绿色工厂申报saas·能碳管理平台
带多刺的玫瑰5 小时前
Leecode#15刷题之三数之和
算法·leetcode·职场和发展
哭泣方源炼蛊5 小时前
并查集进阶 P1(带权并查集,并查集分类)
数据结构·c++·算法·二进制·带权并查集
牧羊人.3336 小时前
动手学深度学习 02 | 手写数字识别
图像处理·人工智能·深度学习·算法
武帝为此6 小时前
【InnoDB存储引擎介绍】
数据库·算法
shehuiyuelaiyuehao7 小时前
算法32,连续数组,前缀和+哈希表
算法·leetcode·职场和发展