C++实现并查集

C++实现并查集

C++实现并查集
视频讲解参考王道考研
链接如下
【王道计算机考研 数据结构】 https://www.bilibili.com/video/BV1b7411N798/?p=55&share_source=copy_web&vd_source=734d9fe130776e8ae82b2b5371a5f5b8

一、并查集

集合功能只实现“并”,没有“交”和“补”
再实现“查”
在这里插入图片描述

我这里
两个数组,一个保存元素,一个是int保存前驱(上一个结点的数组下标)
用数组保存树,如果前驱为负数,认为是头结点,且前驱的绝对值代表本子集元素个数
如果两个元素的头结点相同,认为在一个集合中
这是王道考研讲的方法,“并”的实现比较容易,只需要头结点的前驱改变即可

二、代码如下

1.实现代码

代码如下(示例):

#pragma once
#include<iostream>
using namespace std;
//一个最多能处理的数量
#define MAX_ELEMENT_NUMBER 32

//我的并查集
template<typename elemtype>
class My_Disjoint_set
{
protected:
	elemtype elements[MAX_ELEMENT_NUMBER];//保存的所有元素
	int path[MAX_ELEMENT_NUMBER];//元素对应的前驱	
	int number = 0;//保存的元素个数
public:

	void My_out();

	/*功能:向并查集中插入一个元素
	* 输入:要插入的元素
	* 返回:
	* 其它:
	*/
	void My_insert(elemtype e);

	/*功能:查找位置x的元素所属集合(对应的根节点位置)
	* 输入:
	* x:要查找的元素的位置(数组下标)
	* 返回:根节点数组下标
	* 其它:
	*/
	int My_find(int x);
	/*功能:查找位置x的元素所属集合(对应的根节点位置)
	* 输入:
	* x:要查找的元素的位置(数组下标)
	* 返回:根节点数组下标
	* 其它:会进行压缩路径
	*/
	int My_find_c(int x);


	/*功能:合并两个元素对应的集合
	* 输入:
	* a:要合并的元素的位置(数组下标)
	* b:要合并的元素的位置(数组下标)
	* 返回:
	* 其它:
	*/
	void My_union(int a, int b);

	/*功能:查看两个元素是否属于一个集合(根节点是否相同)
	* 输入:
	* a:第一个元素的位置(数组下标)
	* b:第二个元素的位置(数组下标)
	* 返回:
	* 其它:
	*/
	bool same_set(int a, int b);
};

template<typename elemtype>
void My_Disjoint_set<elemtype>::My_out()
{
	for (int i = 0; i < this->number; i++)
		cout << this->elements[i] << ' ' << this->path[i]<< endl;
	cout << endl;
}

/*功能:向并查集中插入一个元素
	* 输入:要插入的元素
	* 返回:
	* 其它:
	*/
template<typename elemtype>
void My_Disjoint_set<elemtype>::My_insert(elemtype e)
{
	if (MAX_ELEMENT_NUMBER >= number + 1)
	{
		this->elements[this->number] = e;
		this->path[this->number] = -1;
		this->number++;
	}	
}

/*功能:查找位置x的元素所属集合(对应的根节点位置)
	* 输入:
	* x:要查找的元素的位置(数组下标)
	* 返回:根节点数组下标
	* 其它:
	*/
template<typename elemtype>
int My_Disjoint_set<elemtype>::My_find(int x)
{
	//循环寻找x的根
	while (path[x] >= 0)//根结点对应的path小于0
	{
		x = path[x];
	}
	return x;
}
/*功能:查找位置x的元素所属集合(对应的根节点位置)
	* 输入:
	* x:要查找的元素的位置(数组下标)
	* 返回:根节点数组下标
	* 其它:会压缩路径
	*/
template<typename elemtype>
int My_Disjoint_set<elemtype>::My_find_c(int x)
{
	int root = My_find(x);
	while (x != root)
	{
		int t = path[x];
		path[x] = root;
		x = t;
	}
	return root;
}

/*功能:合并两个元素对应的集合
* 输入:
* a:要合并的元素的位置(数组下标)
* b:要合并的元素的位置(数组下标)
* 返回:
* 其它:
*/
template<typename elemtype>
void My_Disjoint_set<elemtype>::My_union(int a, int b)
{
	int root_a = My_find(a);
	int root_b = My_find(b);
	//从属于不同集合(根节点不同)才合并
	if (root_a != root_b)
	{
		//看看那个树结点少,把小树合并到大树
		if (abs(path[root_a]) > abs(path[root_b]))
		{
			path[root_a] += path[root_b];//累加结点总数
			path[root_b] = root_a;//合并
		}
		else
		{
			path[root_b] += path[root_a];//累加结点总数
			path[root_a] = root_b;//合并
		}
	}
}

/*功能:查看两个元素是否属于一个集合(根节点是否相同)
* 输入:
* a:第一个元素的位置(数组下标)
* b:第二个元素的位置(数组下标)
* 返回:
* 其它:
*/
template<typename elemtype>
bool My_Disjoint_set<elemtype>::same_set(int a, int b)
{
	int root_a = My_find(a);
	int root_b = My_find(b);
	if (root_a == root_b)
		return true;
	else
		return false;
}

2.测试案例

代码如下(示例):

#include<iostream>
using namespace std;
#include "My_Disjoint_set.cpp"
#include <string>

void test_1()
{
	My_Disjoint_set<int> dis_set1;
	dis_set1.My_insert(0);
	dis_set1.My_insert(1);
	dis_set1.My_insert(2);
	dis_set1.My_insert(3);
	dis_set1.My_insert(4);
	dis_set1.My_insert(5);
	dis_set1.My_out();
	dis_set1.My_union(1, 2);
	dis_set1.My_union(1, 2);
	dis_set1.My_union(2, 3);
	dis_set1.My_out();
}
void test_2()
{
	My_Disjoint_set<string> dis_2;
	dis_2.My_insert("v0");
	dis_2.My_insert("v1");
	dis_2.My_insert("v2");
	dis_2.My_insert("v3");
	dis_2.My_insert("v4");
	dis_2.My_insert("v5");
	dis_2.My_insert("v6");
	dis_2.My_insert("v7");
	dis_2.My_insert("v8");
	dis_2.My_insert("v9");
	dis_2.My_union(0, 1);
	dis_2.My_union(2, 3);
	dis_2.My_union(4, 5);
	dis_2.My_union(6, 7);
	dis_2.My_union(8, 9);
	dis_2.My_union(4, 7);
	dis_2.My_union(4, 9);
	dis_2.My_union(1, 2);
	dis_2.My_union(3, 5);
	dis_2.My_union(4, 6);
	dis_2.My_out();
	cout << dis_2.My_find_c(0) << endl;
	dis_2.My_out();
}

int main(void)
{
	/*test_1();*/
	test_2();
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

菜哥万岁万岁万万岁

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值