acwing算法基础之数据结构--并查集算法

目录

  • [1 基础知识](#1 基础知识)
  • [2 模板](#2 模板)
  • [3 工程化](#3 工程化)

1 基础知识

并查集支持O(1)时间复杂度实现:

  1. 将两个集合合并。
  2. 询问两个元素是否在一个集合中。

基本原理:每个集合用一颗树来表示。树根的编号就是整个集合的编号。每个结点存储它的父结点,p[x]表示x的父结点。

问题1:如何判断树根:p[x] == x

问题2:如何求x的集合编号:while (p[x] != x) x = p[x];。上述为朴素做法,可以通过路径压缩,进行优化。

cpp 复制代码
int find(int x) {
	if (p[x] != x) p[x] = find(p[x]);
	return p[x];
}

问题3:如何合并两个集合:px是x的集合编号,py是y的集合编号:p[px] = py

2 模板

cpp 复制代码
(1)朴素并查集:

    int p[N]; //存储每个点的祖宗节点

    // 返回x的祖宗节点
    int find(int x)
    {
        if (p[x] != x) p[x] = find(p[x]);
        return p[x];
    }

    // 初始化,假定节点编号是1~n
    for (int i = 1; i <= n; i ++ ) p[i] = i;

    // 合并a和b所在的两个集合:
    p[find(a)] = find(b);

(2)维护size的并查集:

    int p[N], size[N];
    //p[]存储每个点的祖宗节点, size[]只有祖宗节点的有意义,表示祖宗节点所在集合中的点的数量

    // 返回x的祖宗节点
    int find(int x)
    {
        if (p[x] != x) p[x] = find(p[x]);
        return p[x];
    }

    // 初始化,假定节点编号是1~n
    for (int i = 1; i <= n; i ++ )
    {
        p[i] = i;
        size[i] = 1;
    }

    // 合并a和b所在的两个集合:
    size[find(b)] += size[find(a)];
    p[find(a)] = find(b);

(3)维护到祖宗节点距离的并查集:

    int p[N], d[N];
    //p[]存储每个点的祖宗节点, d[x]存储x到p[x]的距离

    // 返回x的祖宗节点
    int find(int x)
    {
        if (p[x] != x)
        {
            int u = find(p[x]);
            d[x] += d[p[x]];
            p[x] = u;
        }
        return p[x];
    }

    // 初始化,假定节点编号是1~n
    for (int i = 1; i <= n; i ++ )
    {
        p[i] = i;
        d[i] = 0;
    }

    // 合并a和b所在的两个集合:
    p[find(a)] = find(b);
    d[find(a)] = distance; // 根据具体问题,初始化find(a)的偏移量

3 工程化

cpp 复制代码
class UnionFind {
public:
	UnionFind(int n) {
		this->n = n;
		p.resize(n);
		cnt.resize(n);
		d.resize(n);
		for (int i = 0; i < n; ++i) {
			p[i] = i;
			cnt[i] = 1;
			d[i] = 0;
		}
	}

	int find(int x) {
		if (x != p[x]) {
			int u = find(p[x]);
			d[x] += d[p[x]];
			p[x] = u;
		}
		return p[x];
	}

	void merge(int x, int y) {
		int px = find(x), py = find(y);
		if (px != py) {
			p[px] = py;
			cnt[py] += cnt[px];		
		}
		return;
	}
	
	int size(int x) {//返回x所在集合的大小
	    return cnt[find(x)];
	}
private:
	int n;
	vector<int> p; //存储父结点
	vector<int> cnt; //存储集合大小,根结点的cnt才有意义
	vector<int> d; //存储到根结点的距离
};
相关推荐
AlenTech9 分钟前
152. 乘积最大子数组 - 力扣(LeetCode)
算法·leetcode·职场和发展
Piar1231sdafa21 分钟前
基于yolo13-C3k2-RVB的洗手步骤识别与检测系统实现_1
人工智能·算法·目标跟踪
做科研的周师兄22 分钟前
【MATLAB 实战】|多波段栅格数据提取部分波段均值——批量处理(NoData 修正 + 地理信息保真)_后附完整代码
前端·算法·机器学习·matlab·均值算法·分类·数据挖掘
天赐学c语言36 分钟前
1.18 - 滑动窗口最大值 && 子类的指针转换为父类的指针,指针的值是否会改变
数据结构·c++·算法·leecode
黑不溜秋的42 分钟前
C++ 线性探测法哈希表
数据结构·散列表
alonewolf_991 小时前
Redis7底层数据结构深度解析:从源码透视高性能设计精髓
数据结构·redis·分布式架构
甄心爱学习1 小时前
KMP算法(小白理解)
开发语言·python·算法
杭州杭州杭州1 小时前
数据结构与算法(5)---二叉树
数据结构
万象.1 小时前
redis数据结构list的基本指令
数据结构·redis·list
zephyr051 小时前
C++ STL unordered_set 与 unordered_map 完全指南
开发语言·数据结构·c++