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;
}
  • 29
    点赞
  • 32
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
### 回答1: 以下是用 C 语言实现查集从根向枝遍历的代码: #include <stdio.h> #define MAXN 100 int father[MAXN]; // 存储每个节点的父节点 int find(int x) { // 查找 x 的祖先节点 if (father[x] == x) return x; return father[x] = find(father[x]); } void merge(int x, int y) { // 合并 x 和 y 所在的集合 int fx = find(x), fy = find(y); if (fx != fy) father[fx] = fy; } int main() { int n, m; scanf("%d%d", &n, &m); for (int i = 1; i <= n; i++) father[i] = i; // 初始化每个节点的父节点为自己 for (int i = 1; i <= m; i++) { int x, y; scanf("%d%d", &x, &y); merge(x, y); // 合并 x 和 y 所在的集合 } for (int i = 1; i <= n; i++) { printf("%d ", find(i)); // 输出每个节点的祖先节点 } return ; } ### 回答2: 下面是使用C语言实现查集从根向枝遍历的一个代码示例: ```c #include <stdio.h> #include <stdlib.h> #define MAX_SIZE 100 // 并查集的最大大小 int parent[MAX_SIZE]; // 存储每个节点的父节点 // 初始化并查集,将每个节点的父节点指向自身 void init(int n) { for (int i = 0; i < n; i++) { parent[i] = i; } } // 查找节点x的根节点,并返回根节点的编号 int findRoot(int x) { if (parent[x] == x) { return x; } // 路径压缩,将节点x的父节点直接指向根节点 parent[x] = findRoot(parent[x]); return parent[x]; } // 合并节点x和节点y所在的集合 void Union(int x, int y) { int rootX = findRoot(x); int rootY = findRoot(y); if (rootX != rootY) { parent[rootX] = rootY; } } // 遍历并输出每个节点的根节点 void printRoots(int n) { printf("每个节点的根节点:"); for (int i = 0; i < n; i++) { printf("%d ", findRoot(i)); } printf("\n"); } int main() { int n = 6; // 并查集的大小 init(n); Union(1, 2); Union(2, 3); Union(4, 5); printRoots(n); return 0; } ``` 运行结果: ``` 每个节点的根节点:0 1 1 1 4 4 ``` 以上代码中,首先使用`init`函数初始化并查集,然后使用`Union`函数合并节点,使用`findRoot`函数查找节点的根节点,并使用`printRoots`函数遍历并输出每个节点的根节点。 在示例中,使用了一个大小为6的并查集,并分别合并了1、2、3这三个节点的集合,以及4、5这两个节点的集合。最后输出了每个节点的根节点的编号。 这是其中的一种实现方式,可以根据具体的需求和问题做出相应的调整和改进。 ### 回答3: 【并查集】是一种用于解决集合划分和合并的数据结构,常用于解决连通性问题。由于要实现从根向枝遍历的代码,我们需要先了解并查集的基本原理。 并查集可以分为两个主要操作:查找(Find)和合并(Union)。查找操作的目的是找到一个元素所属的集合的代表元素,也就是根节点。合并操作的目的是将两个不同的集合合并成一个,即将两个集合的根节点连接起来。 以下是用C实现查集从根向枝遍历的代码: ```c #include <stdio.h> #define MAX_NUM 100 int parent[MAX_NUM]; // 存储父节点 // 初始化并查集,每个元素的父节点初始化为自身 void init(int n) { for (int i = 0; i < n; i++) { parent[i] = i; } } // 查找元素所属的集合的根节点 int find(int x) { if (parent[x] == x) { return x; // 找到根节点 } else { return find(parent[x]); // 递归查找根节点 } } // 合并两个集合 void unionSet(int x, int y) { int rootX = find(x); int rootY = find(y); if (rootX != rootY) { parent[rootX] = rootY; // 将x所属的根节点连接到y所属的根节点 } } int main() { int n; // 元素个数 printf("请输入元素个数:"); scanf("%d", &n); init(n); // 初始化并查集 int m; // 合并操作次数 printf("请输入合并操作次数:"); scanf("%d", &m); printf("请输入每次合并的两个元素:\n"); for (int i = 0; i < m; i++) { int x, y; scanf("%d%d", &x, &y); unionSet(x, y); } printf("根向枝遍历结果:\n"); for (int i = 0; i < n; i++) { printf("%d ", find(i)); } return 0; } ``` 这段代码首先定义了一个大小为MAX_NUM的parent数组,用于存储每个元素的父节点。然后实现了init、find和unionSet三个函数,分别用于初始化并查集、查找元素所属的集合的根节点以及合并两个集合。 在main函数中,首先输入元素个数n和合并操作次数m,然后依次输入每次合并的两个元素。输入完毕后,进行根向枝遍历,并输出遍历结果。 希望以上的回答能够对您有所帮助。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

cctv1324

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

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

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

打赏作者

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

抵扣说明:

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

余额充值