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; //存储到根结点的距离
};
相关推荐
Voyager_49 小时前
算法学习记录03——二叉树学习笔记:从两道题看透后序位置的关键作用
笔记·学习·算法
我搞slam14 小时前
快乐数--leetcode
算法·leetcode·哈希算法
WWZZ202514 小时前
快速上手大模型:机器学习3(多元线性回归及梯度、向量化、正规方程)
人工智能·算法·机器学习·机器人·slam·具身感知
东方佑15 小时前
从字符串中提取重复子串的Python算法解析
windows·python·算法
西阳未落16 小时前
LeetCode——二分(进阶)
算法·leetcode·职场和发展
通信小呆呆16 小时前
以矩阵视角统一理解:外积、Kronecker 积与 Khatri–Rao 积(含MATLAB可视化)
线性代数·算法·matlab·矩阵·信号处理
Dfreedom.16 小时前
一文掌握Python四大核心数据结构:变量、结构体、类与枚举
开发语言·数据结构·python·变量·数据类型
CoderCodingNo17 小时前
【GESP】C++四级真题 luogu-B4068 [GESP202412 四级] Recamán
开发语言·c++·算法
一个不知名程序员www17 小时前
算法学习入门---双指针(C++)
c++·算法
Shilong Wang18 小时前
MLE, MAP, Full Bayes
人工智能·算法·机器学习