05——并查集

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/zxt_1/article/details/85391131

1.可以解决连接问题,时间复杂度近似为O(n)

2.支持的操作

unionElements(p, q);		//将集合p、q归并
find(p);					//查找p属于哪个集合
isConnected(p,q);			//判断元素p和元素q是否所属一个集合

3.实现1(quick find)

数组中存储的是哪个集合

在这里插入图片描述

class UnionFind {

private:
	int *id; 
	int count; // 数据个数
public:
	// 构造函数
	UnionFind(int n) {
		count = n;
		id = new int[n];
		// 初始化, 每一个id[i]指向自己, 没有合并的元素
		for (int i = 0; i < n; i++)
			id[i] = i;
	}
	
	// 析构函数
	~UnionFind() {
		delete[] id;
	}
	
	// 查找过程, 查找元素p所对应的集合编号
	int find(int p) {
		assert(p >= 0 && p < count);
		return id[p];
	}
	
	// 查看元素p和元素q是否所属一个集合
	bool isConnected(int p, int q) {
		return find(p) == find(q);
	}
	
	// 合并元素p和元素q所属的集合
	void unionElements(int p, int q) {
		int pID = find(p);
		int qID = find(q);
		if (pID == qID)
			return;
		// 合并过程需要遍历一遍所有元素, 将两个元素的所属集合编号合并
		for (int i = 0; i < count; i++)
			if (id[i] == pID)
				id[i] = qID;
		}
};

4.实现2(quick union)

数组中存储的父节点的下标

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

class UnionFind{

private:
	int* parent;// parent[i]表示第i个元素所指向的父节点
	int count; // 数据个数
public:
	UnionFind(int count){
		parent = new int[count];
		this->count = count;
		// 初始化, 每一个parent[i]指向自己, 表示每一个元素自己自成一个集合
		for( int i = 0 ; i < count ; i ++ )
			parent[i] = i;
	}

	~UnionFind(){
		delete[] parent;
	}
	
	// 查找过程, 查找元素p所对应的集合编号
	int find(int p){
		assert( p >= 0 && p < count );
		// 不断去查询自己的父亲节点, 直到到达根节点
		// 根节点的特点: parent[p] == p
		while( p != parent[p] )
			p = parent[p];
		return p;
	}
	
	// 查看元素p和元素q是否所属一个集合
	bool isConnected( int p , int q ){
		return find(p) == find(q);
	}
	
	// 合并元素p和元素q所属的集合
	void unionElements(int p, int q){            //缺陷:总是将p的根节点指向q的根节点
		int pRoot = find(p);
		int qRoot = find(q);
		if( pRoot == qRoot )
			return;
		parent[pRoot] = qRoot;
	}
};

优化:unionElements函数

方式1:将节点少的根指向节点多的根

class UnionFind{

private:
	int* parent; // parent[i]表示第i个元素所指向的父节点
	int* sz; // sz[i]表示以i为根的集合中元素个数
	int count; // 数据个数
public:
	UnionFind(int count){
		parent = new int[count];
		sz = new int[count];
		this->count = count;
		for( int i = 0 ; i < count ; i ++ ){
			parent[i] = i;
			sz[i] = 1;
		}
	}

	~UnionFind(){
		delete[] parent;
		delete[] sz;
	}
	
	// 查找过程, 查找元素p所对应的集合编号
	int find(int p){
		assert( p >= 0 && p < count );
		// 不断去查询自己的父亲节点, 直到到达根节点
		// 根节点的特点: parent[p] == p
		while( p != parent[p] )
			p = parent[p];
		return p;
	}
	
	// 查看元素p和元素q是否所属一个集合
	bool isConnected( int p , int q ){
		return find(p) == find(q);
	}
	
	// 合并元素p和元素q所属的集合
	void unionElements(int p, int q){
		int pRoot = find(p);
		int qRoot = find(q);
		if( pRoot == qRoot )
			return;
		// 根据两个元素所在树的元素个数不同判断合并方向
		// 将元素个数少的集合合并到元素个数多的集合上
		if( sz[pRoot] < sz[qRoot] ){
			parent[pRoot] = qRoot;
			sz[qRoot] += sz[pRoot];
		}
		else{
			parent[qRoot] = pRoot;
			sz[pRoot] += sz[qRoot];
		}
	}
};

方式2:考虑层级

在这里插入图片描述

在这里插入图片描述

class UnionFind{

private:
	int* rank; // rank[i]表示以i为根的集合所表示的树的层数
	int* parent; // parent[i]表示第i个元素所指向的父节点
	int count; // 数据个数
	
public:
	UnionFind(int count){
		parent = new int[count];
		rank = new int[count];
		this->count = count;
		for( int i = 0 ; i < count ; i ++ ){
			parent[i] = i;
			rank[i] = 1;
		}
	}

	~UnionFind(){
		delete[] parent;
		delete[] rank;
	}
	
	// 查找过程, 查找元素p所对应的集合编号
	int find(int p){
		assert( p >= 0 && p < count );
		// 不断去查询自己的父亲节点, 直到到达根节点
		// 根节点的特点: parent[p] == p
		while( p != parent[p] )
			p = parent[p];
			return p;
	}
	
	// 查看元素p和元素q是否所属一个集合
	bool isConnected( int p , int q ){
		return find(p) == find(q);
	}
	
	// 合并元素p和元素q所属的集合
	void unionElements(int p, int q){
		int pRoot = find(p);
		int qRoot = find(q);
		if( pRoot == qRoot )
			return;
	
		if( rank[pRoot] < rank[qRoot] ){
			parent[pRoot] = qRoot;
		}
		else if( rank[qRoot] < rank[pRoot]){
			parent[qRoot] = pRoot;
		}	
		else{ // rank[pRoot] == rank[qRoot]
			parent[pRoot] = qRoot;
			rank[qRoot] += 1; // 此时, 我维护rank的值
		}	
	}
};

优化:find函数(路径压缩)

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

class UnionFind{

private:
	int* rank;// rank[i]表示以i为根的集合所表示的树的层数
	int* parent; // parent[i]表示第i个元素所指向的父节点
	int count; // 数据个数
	
public:
	UnionFind(int count){
		parent = new int[count];
		rank = new int[count];
		this->count = count;
		for( int i = 0 ; i < count ; i ++ ){
			parent[i] = i;
			rank[i] = 1;
		}
	}
	
	~UnionFind(){
		delete[] parent;
		delete[] rank;
	}
	
	// 查找过程, 查找元素p所对应的集合编号
	int find(int p){
		assert( p >= 0 && p < count );
		// path compression 1
		while( p != parent[p] ){
			parent[p] = parent[parent[p]];
			p = parent[p];
		}
		return p;
		// path compression 2, 递归算法
		// if( p != parent[p] )
		// parent[p] = find( parent[p] );
		// return parent[p];
	}
	
	// 查看元素p和元素q是否所属一个集合
	bool isConnected( int p , int q ){
		return find(p) == find(q);
	}
	
	// 合并元素p和元素q所属的集合
	void unionElements(int p, int q){
		int pRoot = find(p);
		int qRoot = find(q);
		if( pRoot == qRoot )
			return;
	
		if( rank[pRoot] < rank[qRoot] ){
			parent[pRoot] = qRoot;
		}
		else if( rank[qRoot] < rank[pRoot]){
			parent[qRoot] = pRoot;
		}
		else{ // rank[pRoot] == rank[qRoot]
			parent[pRoot] = qRoot;
			rank[qRoot] += 1; // 此时, 我维护rank的值
		}
}
//最优情况(path compression 2, 递归算法)

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/zxt_1/article/details/85391131