刷题笔记:并查集(LeetCode1319)

本文详细介绍了并查集这种数据结构,包括其初始化、查找和合并操作,并展示了如何通过路径压缩进行优化。此外,通过LeetCode 1319题为例,解释了并查集在解决实际问题中的应用,即计算使网络连接所需的最小移动线缆数。
摘要由CSDN通过智能技术生成

并查集定义

并查集(Union合并,Find查找,Set集合)是一种维护集合的数据结构,并查集支持一下两个操作:

  • 合并:合并两个集合
  • 查找:判断两个原色是否在用一个集合(寻找根结点)

并查集通过一个一维数组 int father[N] 实现。其中father[i] 表示节点 i 的父结点。如果,father[i] == i ,则说明结点 i 为根结点;对于同一个结合,只有一个根结点,将其作为一个集合的标识。
在这里插入图片描述
以上图为例,图中分为了两个集合,(1,2,5)为一个集合,(3,4)为一另一个结合,有两个根节结点。

father[1]=1;	//1的父结点为1 
father[2]=1;	//2的父结点为1 
father[3]=3;
father[4]=3;
father[5]=1;

并查集的基本工作

1、初始化
一开始,每一个结点都是独立的一个集合,令father[i]=i。

void init(int n)
{
	for(int i=1;i<=n;i++){
		father[i]=i;
		isroot[i]=false;
	}
}

2、查找
规定同一个集合中只存在一个根结点,因此查找操作就是对给定的结点寻找其根结点的过程,可以通过递推或者递归实现。

  • 递推
int find_father(int x)	//返回结点x所在集合的根结点
{
	while(x!=father[x]){
		x=father[x];
	} 
	return x;
}
  • 递归
int find_father(int x)	//返回结点x所在集合的根结点
{
	if(x==father[x]) return x;
	else return find_father(father[x]);
}

3、合并
合并是指将两个集合合并成一个集合。题目中一般给出两个元素a和b,对其进行合并操作;首先需要判断a和b是否在同一个集合中,如果在则不需要再进行合并了;如果不在, 则进行合并,将其中一个集合的根节点的父亲指向另一个集合的根结点。

void Union(int a,int b)
{
	int faa=find_father(a);
	int fab=find_father(b);
	if(faa!=fab) father[faa]=fab;
}

路径压缩

上诉的查找函数是没有经过优化的。观察下面的图片。
在这里插入图片描述
在上图中,为了查找集合的根结点,我们可以对其进行变化。这样相当于把当前查询结点的路径上的所有结点的父亲都指向根结点,查找时就不需要回溯了,得到以下优化后 的路径压缩代码。

  • 递推
int find_father(int x)
{
	int a=x;
	while(x!=father[x]){
		x=father[x];
	}
	while(a!=father[a]){	//x存放的是根结点 
		int z=a;			//a值被覆盖 
		a=father[a];		//回溯到父结点 
		father[z]=x; 
	}
	return x
}
  • 递归
int find_father(int x)
{
	if(x==father[x]) return x;
	else{
		int f=find_father(father[x]);
		father[x]=f;
		return f;
	}
}

题目-LeetCode1319

该题为并查集类,答题过程与并查集解决的模板差别不大。以下为原题链接。
https://leetcode-cn.com/problems/number-of-operations-to-make-network-connected/
题目需要求出最少需要移动的数目,我们在通过并查集的相关操作,将计算机分成cnt个集合以后,分析可知,每个集合之间只需要添加一条线缆即可连接,即最少移动的线缆条数为cnt-1条。同时我们也要注意,线缆的条数至少为n-1条,才可以将n个计算机连接起来。
具体代码如下所示。

class Solution {
public:
    int father[100005];
    bool isroot[100005];
    void init(int n){
        for(int i=0;i<n;i++){
            father[i]=i;
            isroot[i]=false;
        }
    }
    int find_father(int x){
        if(x==father[x]) return x;
        else{
            int f=find_father(father[x]);
            father[x]=f;
            return f;
        }
    }
    void Union(int a,int b){
        int fa=find_father(a);
        int fb=find_father(b);
        if(fa!=fb) father[fa]=fb;
    }
    int makeConnected(int n, vector<vector<int>>& connections) {
        int len=connections.size();
        if(len+1<n) return -1;  //分析可知,至少需要n-1条线缆
        int cnt=0;
        init(n);
        for(int i=0;i<len;i++){
            Union(connections[i][0],connections[i][1]);
        }
        for(int i=0;i<n;i++) isroot[find_father(i)]=true;
        for(int i=0;i<n;i++) if(isroot[i]) cnt++;
        return cnt-1;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值