数据结构之并查集


一、并查集是什么?

并查集是一种树型的数据结构,用于处理一些不相交集合(Disjoint Sets)的合并及查询问题。
并查集算法(union-find algorithm)定义了两个用于此数据结构的操作:

  1. Find:确定元素属于哪一个子集。这个确定方法就是不断向上查找找到它的根节点,它可以被用来确定两个元素是否属于同一子集。
  2. Union:将两个子集合并成同一个集合。
  3. MakeSet:用于建立单元素集合。

为了更加精确的定义这些方法,需要定义如何表示集合。一种常用的策略是为每个集合选定一个固定的元素,称为代表,以表示整个集合,即树的根节点。接着,Find(x) 返回 x 所属集合的代表,而 Union 使用两个集合的代表作为参数,将两个树进行合并。

二、伪码实现

并查集(树)是一种将一个集合以树形结构进行组合的数据结构,如下图所示。其中每一个节点保存着到它的父节点的引用。
在这里插入图片描述

伪代码实现如下:

function MakeSet(x)
    x.parent := x
 
function Find(x)
    if x.parent == x
       return x
    else
       return Find(x.parent)
 
function Union(x, y)
    xRoot := Find(x)
    yRoot := Find(y)
    xRoot.parent := yRoot

通过这种方法创建的树可能会严重不平衡,可以用下面方法优化。

1.按秩合并

总是将更小的树连接至更大的树上。因为影响运行时间的是树的深度,更小的树添加到更深的树的根上将不会增加秩除非它们的秩相同。在这个算法中,术语“秩”替代了“深度”,因为同时应用了路径压缩时(见下文)秩将不会与高度相同。单元素的树的秩定义为0,当两棵秩同为r的树联合时,它们的秩r+1。如下图所示:
在这里插入图片描述
伪码实现如下:

function MakeSet(x)
    x.parent := x
    x.rank   := 0
 
function Union(x, y)
    xRoot := Find(x)
    yRoot := Find(y)
    if xRoot == yRoot
        return
 
    // x和y不在同一个集合,合并它们。
    if xRoot.rank < yRoot.rank
        xRoot.parent := yRoot
    else if xRoot.rank > yRoot.rank
        yRoot.parent := xRoot
    else
        yRoot.parent := xRoot
        xRoot.rank := xRoot.rank + 1  

2. 路径压缩

在执行“查找”时扁平化树结构的方法。关键在于在路径上的每个节点都可以直接连接到根上;他们都有同样的表示方法。为了达到这样的效果,Find递归地经过树,改变每一个节点的引用到根节点。得到的树将更加扁平,为以后直接或者间接引用节点的操作加速。
在这里插入图片描述

伪码实现如下:

function Find(x)
    if x.parent != x
       x.parent := Find(x.parent)
    return x.parent 

三、C++代码实现

class UnionFind{
private:
	vector<int> parent;
	vector<int> rank;
public: 
	//构造函数
	UnionFind(int n){
		parent=vector<int>(n);
		for(int i=0;i<n;++i){
			parent[i]=i; //每个节点的父节点初始化为自己
		}
		rank=vector<int>(n,0);
	}
	int find(int i){ //路径压缩
		if(parent[i]!=i){
			parent[i]=find(parent[i]); //在find的过程中递归实现
		}
		return parent[i];
	}
	void union(int i, int j){ //按秩合并
		int rooti=find(i);
		int rootj=find(j);
		if (rooti==rootj) return;
		// 让rooti节点为最后的根节点
		if (rank[rooti]<rank[rootj]){
			swap(rooti,rootj);
		}
		if(rank[rooti]==rank[rootj]){
			rank[rooti]++;
		}
		parent[rootj]=rooti;
	}
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值