数据结构之并查集(C++)

一、并查集

存储结构:数组
.    原理:
.        1.构建目标:利用数组的快速寻址的优势,将多个集合在数组上构建成无环非连通图,
.                    各个连通分量的结点间以保存上级结点索引进行关联,
.                    即树形结构,每个集合有且只有一个根结点;
.        2.根结点查找:查找集合元素的根结点;
.        3.连通性判断:通过查找两个集合元素的根结点,判断两集合是否连通;
.        4.集合合并:将两个非连通的集合合并,选取其中任一集合的根结点作为合并后集合的根结

.    用途:

.        1.查:快速判断两个元素所在集合是否连通,压缩路径后,时间复杂度为 O(1);

.        2.并:快速合并n个集合中存在交集的集合,最终统计出无交集集合个数。

二、代码

//文件名:"UFS.h"
#pragma once
#ifndef UFS_H_
#define UFS_H_


//参考博客:
//https://blog.csdn.net/qingdou_aixuexi/article/details/52695395
//https://blog.csdn.net/niushuai666/article/details/6662911
//https://blog.csdn.net/u014682691/article/details/52004316
//https://blog.csdn.net/wusecaiyun/article/details/49079701

/*
.	并查集类 (Union Find Set)
.	存储结构:数组
.	原理:
.		1.构建目标:利用数组的快速寻址的优势,将多个集合在数组上构建成无环非连通图,
.				    各个连通分量的结点间以保存上级结点索引进行关联,
.				    即树形结构,每个集合有且只有一个根结点;
.		2.根结点查找:查找集合元素的根结点;
.		3.连通性判断:通过查找两个集合元素的根结点,判断两集合是否连通;
.		4.集合合并:将两个非连通的集合合并,选取其中任一集合的根结点作为合并后集合的根结点;
*/

class UFS
{
private:
	int * arr = NULL;			//数组指针
	//int * arr_num = NULL;		//各结点的孩子数计数数组
	int length = 0;				//数组长度

public:
	UFS(int length);					//构造函数
	~UFS();								//析构函数
	int FindRoot(int e);				//寻找元素所在集合的根
	int Union(int e1, int e2);			//合并集合
};

#endif // !UFS_H_
//文件名:"UFS.cpp"
#include "stdafx.h"
#include "UFS.h"
using namespace std;

UFS::UFS(int length)
{
	/*
	.	构造函数
	*/
	//0.初始化数组长度
	this->length = length;
	//1.创建数组对象
	arr = new int[length];
	//2.初始化数组:其下标值为下标索引
	for (int i = 0; i < length; i++)
	{
		arr[i] = i;
	}
}

UFS::~UFS()
{
	/*
	.	析构函数
	*/
	//销毁数组
	delete[] arr;
}

int UFS::FindRoot(int e)
{
	/*
	.	寻找元素所在集合的根
	.		注:根结点位置:arr[i] == i;其它结点位置:arr[i] == pi (上级结点下标)
	*/
	//遍历向上级寻根
	while (arr[e] != e)
	{
		e = arr[e];
	}
	return e;
}

int UFS::Union(int e1, int e2)
{
	/*
	.	合并集合
	.	入参:
	.		int e1, e2: 两结点元素下标
	.	出参:
	.		int : 0|集合连通不合并  1|集合非连通合并
	*/
	//1.分别查找e1 e2 元素集合根
	int r1 = FindRoot(e1);
	int r2 = FindRoot(e2);
	//2.若两集合连通,不合并;否则合并
	if (r1 == r2)
		return 0;
	//3.合并集合(右集合根结点指向左集合根结点)
	arr[r2] = r1;
	//4.返回合并成功
	return 1;
}
//文件名:"UFS_Test.cpp"
#include "stdafx.h"
#include <iostream>
#include "UFS.h"
using namespace std;

int main()
{
	UFS * ufs = new UFS(6);
	ufs->Union(1, 2);
	ufs->Union(2, 3);
	ufs->Union(4, 5);

	cout << "显示并查集结点:" << endl;
	for (int i = 0; i < 6; i++)
	{
		cout << i << "(" << ufs->FindRoot(i) << ") ";
	}
	cout << endl;

	ufs->Union(3, 5);

	cout << "合并(3,5)后,显示并查集结点:" << endl;
	for (int i = 0; i < 6; i++)
	{
		cout << i << "(" << ufs->FindRoot(i) << ") ";
	}
	
	return 0;
}

三、输出结果 

四、参考博客

1.仅集合根结点处记录孩子数,并用负数:

https://blog.csdn.net/qingdou_aixuexi/article/details/52695395

2.很形象的“畅通工程”问题,“寻根”操作中用到了压缩路径(即压缩成子结点直指根结点):
https://blog.csdn.net/niushuai666/article/details/6662911

3.用两个数组实现,增加了统计各个结点孩子数的数组,合并时以孩子数少的根结点向孩子多的根合并:
https://blog.csdn.net/u014682691/article/details/52004316

4.数组元素对象化,增加每个结点的parent域和孩子数域,也使用了压缩路径:
https://blog.csdn.net/wusecaiyun/article/details/49079701

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值