并查集(Disjoint Set)详解+例题

目录

概述

步骤

例题

题目描述

代码


概述

并查集,顾名思义,需要对集合进行查询与合并。

步骤

1、初始化:把每个点所在集合初始化为其自身
2、查询:查询元素所在的集合,即根节点
3、合并:若集合不同,将两个元素所在的集合合并为一个集合,即根节点合并

并查集是一种树型的数据结构,常常在使用中以森林来表示(可查看树-双亲表示法)。

合并时有个问题,就是树的深度不要太大,查询和合并时可以进行路径压缩,一种是低的树合并到高的树,另一种是合并前根的所有子节点的父节点都改为根

例题

来源:牛客网-KY126 畅通工程

题目描述

    某省调查城镇交通状况,得到现有城镇道路统计表,表中列出了每条道路直接连通的城镇。省政府“畅通工程”的目标是使全省任何两个城镇间都可以实现交通(但不一定有直接的道路相连,只要互相间接通过道路可达即可)。问最少还需要建设多少条道路?

输入描述:

    测试输入包含若干测试用例。每个测试用例的第1行给出两个正整数,分别是城镇数目N ( < 1000 )和道路数目M;随后的M行对应M条道路,每行给出一对正整数,分别是该条道路直接连通的两个城镇的编号。为简单起见,城镇从1到N编号。 
    注意:两个城市之间可以有多条道路相通,也就是说
    3 3
    1 2
    1 2
    2 1
    这种输入也是合法的
    当N为0时,输入结束,该用例不被处理。

输出描述:

    对每个测试用例,在1行里输出最少还需要建设的道路数目。

示例1

输入

4 2
1 3
4 3
3 3
1 2
1 3
2 3
5 2
1 2
3 5
999 0
0

输出

1
0
2
998

代码

全局变量

// 父节点 树深度
int parent[1001];
int height[1001];

初始化函数,使用height压缩

// 初始化
void Init(int N)
{
	for (int i = 1; i < N + 1; i++)
	{
		parent[i] = i;
		height[i] = 0;
	}
}

初始化函数,不使用height压缩 

// 初始化
void Init2(int N)
{
	for (int i = 1; i < N + 1; i++)
	{
		parent[i] = i;
	}
}

查找集合函数

// 查找i所属的集合(根节点)
int FindRoot(int i)
{
	if (parent[i] == i)return i;
	return FindRoot(parent[i]);
}

合并集合函数,使用height压缩

// 合并c1,c2所属集合
void UnionSet(int c1, int c2)
{
	int rc1 = FindRoot(c1);
	int rc2 = FindRoot(c2);
	if (rc1 != rc2) 
	{
		if(height[rc1]<height[rc2])parent[rc1] = rc2;
		else if(height[rc1]>height[rc2])parent[rc2] = rc1;
		else
		{
			parent[rc2] = rc1;
			height[rc1]++;
		}
	}
}

路径压缩函数

// 路径压缩,root节点的所有子节点的父节点均为root
void PathCompression(int root,int p)
{
	int tmp = parent[p];
	while (tmp!=root)
	{
		tmp = parent[p];
		parent[p] = root;
		p = tmp;
	}
}

合并函数,不使用height

// 合并c1,c2所属集合 不使用height
void UnionSet2(int c1, int c2)
{
	int rc1 = FindRoot(c1);
	int rc2 = FindRoot(c2);
	PathCompression(rc1,c1);
	PathCompression(rc2,c2);
	if (rc1 != rc2)
	{
		parent[rc2] = rc1;
	}
}

全部代码

/*
https://www.nowcoder.com/practice/4878e6c6a24e443aac5211d194cf3913?tpId=40&rp=1&ru=%2Factivity%2Foj&qru=%2Fta%2Fkaoyan%2Fquestion-ranking
Project: KY 126畅通工程
Date:    2020/10/
Author:  Frank Yu
*/
#include<algorithm>
#include<iostream>
using namespace std;
// 父节点 树深度
int parent[1001];
int height[1001];
// 初始化
void Init(int N)
{
	for (int i = 1; i < N + 1; i++)
	{
		parent[i] = i;
		height[i] = 0;
	}
}
// 初始化
void Init2(int N)
{
	for (int i = 1; i < N + 1; i++)
	{
		parent[i] = i;
	}
}
// 查找i所属的集合(根节点)
int FindRoot(int i)
{
	if (parent[i] == i)return i;
	return FindRoot(parent[i]);
}
// 合并c1,c2所属集合
void UnionSet(int c1, int c2)
{
	int rc1 = FindRoot(c1);
	int rc2 = FindRoot(c2);
	if (rc1 != rc2) 
	{
		if(height[rc1]<height[rc2])parent[rc1] = rc2;
		else if(height[rc1]>height[rc2])parent[rc2] = rc1;
		else
		{
			parent[rc2] = rc1;
			height[rc1]++;
		}
	}
}
// 路径压缩,root节点的所有子节点的父节点均为root
void PathCompression(int root,int p)
{
	int tmp = parent[p];
	while (tmp!=root)
	{
		tmp = parent[p];
		parent[p] = root;
		p = tmp;
	}
}
// 合并c1,c2所属集合 不使用height
void UnionSet2(int c1, int c2)
{
	int rc1 = FindRoot(c1);
	int rc2 = FindRoot(c2);
	PathCompression(rc1,c1);
	PathCompression(rc2,c2);
	if (rc1 != rc2)
	{
		parent[rc2] = rc1;
	}
}
// 查找集合个数
int CountRoots(int N)
{
	int count = 0;
	for (int i = 1; i < N + 1; ++i)
	{
		if (parent[i] == i)count++;
	}
	return count;
}
//主函数
int main()
{
	int N, M;
	while (cin>>N>>M&&N!=0)
	{
		// 初始化
		//Init(N);
		Init2(N);
		while(M--)
		{
			int c1, c2;
			cin >> c1 >> c2;
			//UnionSet(c1, c2);
			UnionSet2(c1, c2);
		}
		int roots = CountRoots(N);
		cout << roots - 1 << endl;
	}
	return 0;
}

更多数据结构与算法实现:数据结构(严蔚敏版)与算法的实现(含全部代码)

本人b站账号:lady_killer9

有问题请下方评论,转载请注明出处,并附有原文链接,谢谢!如有侵权,请及时联系。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

lady_killer9

感谢您的打赏,我会加倍努力!

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

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

打赏作者

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

抵扣说明:

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

余额充值