力扣684冗余连接

题目一

力扣:684
在这里插入图片描述
在这里插入图片描述

思路 :并查集

并查集

1、一定心中要有数,一旦涉及了二者是否所属同一个集合,尤其在图论里面,一定要想到并查集,并查集可以以很快速的速度查询该元素所属集合,以及合并集合,有时候我们可以根据两个元素是否属于同一个集合来做文章解题,也可以根据集合的个数来解题(即记录集合个数,当集合不断合并只剩下一个时候的情况等等问题。)
2、并查集很好实现分成两块介绍

存储

1、并查集维护的是一些元素所属的集合,为了更快速的知道集合是谁,所以做出来一个主元素,他就指向他自己。
2、并查集内部每一个集合都按照树的数据结构存储,每一个元素存的都是它上一级元素
在这里插入图片描述
在这里插入图片描述

3、初始的时候,每个元素就是单独自己一个集合,因为没有输入任何有关的关系。

功能

1、find()。能够找到每个元素所在集合的代表元,因为在并查集中,每个元素存的都是他在树上的上一个元素,所以按照这个路径查找上去就行了,直到发现自己指向自己。
2、find()函数特别的,要进行压缩查询,对路径上的节点要都一起指向集合的代表元,这样子会更提高查询速度。
3、merge函数,就是将两个集合合并,只要把任意一个集合的代表元连到另外的集合就行

本题思路

读题,在一个树的基础上,再加一笔,一颗树上的节点都属于一个集合,当读入一条边的时候,如果边的两个点同时属于一个树的时候,这个边就是要找的冗余边,上文也说了,一颗树上的节点都属于一个集合,所以就要看两个点属不属于一个集合,这个点与集合从属关系,用来维护点和集合关系的东西,一看到就应该立刻想到并查集。

代码

class unionFindSet {
    
    //封装的并查集类
public:
	//初始化
	unionFindSet(int n) {
    
    
		this->n = n;
		settings.resize(n + 1);
		for (int i = 0; i <= n; i++) {
    
    
			settings[i] = i;
		}
	}

	//find操作
	int find(int item) {
    
    
		if (item <= 0 || item > n) {
    
    //防止越界
			return 0;
		}

		if (settings[item] == item) {
    
    //找到代表元,即自己指向自己
			return item;
		}

		settings[item] = find(settings[item]);//压缩查询,让已知路径上所有点都指向代表元,加速查询
		//将代表元赋予路径上所有节点
		return settings[item];
	}
	
	//merge操作
	void merge(int x, int y) {
    
    
		settings[find(y)] = find(x);//两个集合代表顶点融合
	}


private:
	vector<int> settings;//维护的并查集
	int n;//该并查集大小
};

class Solution {
    
    
public:
	vector<int> findRedundantConnection(vector<vector<int>>& edges) {
    
    
		int n = edges.size();
		unionFindSet uf_set(n);
		int i = 0;
		for (; i < n; i++) {
    
    
			int x = edges[i][0];
			int y = edges[i][1];
			if (uf_set.find(x) != uf_set.find(y)) {
    
    //如果两个点还不在一个集合,说明再构造树,这个边合法
				uf_set.merge(x, y);
			}
			else {
    
    //反之,说明这两个边已经在一个集合了,即在一棵树上了,再加边就是符合题意的冗余边
				break;
			}
		}
		return edges[i];
	}
};

(所有代码均已在力扣上运行无误)

经测试,该代码运行情况是(经过多次测试所得最短时间):

在这里插入图片描述

在这里插入图片描述

附加题目

网络灾难

字节跳动在全国各地都建立了服务器集群,各个服务器之间通过光缆连接形成了网状拓扑结构。
有一天邪恶的外星人入侵了集群,他们想破坏字节跳动的网络。
他们会残忍地切断一条一条网线,直到所有光缆都被切断。
现在给出这个拓扑结构以及外星人切断光缆的顺序,求当外星人切到第几根光缆时,字节跳动的网络会完全断裂成多块。
断裂成多块的定义为:当某一次光缆被切断后,若服务器A和服务器B完全没有光缆直接或间接连接,则称整个网络结构断裂成多块。

输入描述

第一行有两个数n m 表示字节跳动的服务器集群个数,以及光缆的数目。

接下来m行 第1+x行 有两个数 a,b 表示服务器集群a 和 服务器集群b有光缆相连,该光缆编号为x。

接下来还有m行 第1+m+x行 有一个数c,表示外星人在第x步会切断编号为c的光缆。
保证初始网络没有分裂。

输出描述

只有一个数 ans,表示外星人在第ans步切断光缆之后,字节跳动的网络会分裂成多个块。

示例1
输入

4    5
1    2
1    3
1    4
2    3
2    4
1
2
3
4
5

输出

3

说明

当1-21-31-4的光缆被切断后,服务器集群1 和 其他服务器集群完全断裂。

思路

这个题非常的经典,我们这里提供思路和未经检验的代码(代码没经过OJ,可能有错误)
1、我们正向思考,在一堆乱如麻的线路中,每剪掉一根,都得看看是不是连通的,很复杂。
2、一定要有逆向思维,当你发现正向很难走通的时候,我们不妨反过来思考,外星人的目的是什么,按照顺序把所有的线路剪掉,反过来呢,按照逆序连上所有的线路,所以我们可以按照外星人剪线路的逆序,生成图,当某一条线被连上时候,整个图被连通,那么这根线就是外星人在剪线路时候,决定图是否连通的关键。
3、这个题就涉及到了一直加边什么时候才能连通,一张连通图,所有的点必定都是在一个大的集合里,最开始图里面只有点,每个点各自一个集合,随着边的增加,每加一条边,就会少一个集合,这样,到最后答案边加进来的时候,恰好一个集合。
4、综上,涉及到了集合和集合的关系,以及随着边的加入,边就代表着两个点是等价的、在一起的,集合逐渐减少,所以用并查集维护各个集合,最开始多少个点多少个集合,随着边的加入,集合逐渐合并,直到最后一条边使得集合数=1为止。

示例代码(可能有错误,参照思路即可)

//C
#include<stdio.h>
#include<malloc.h>

typedef struct _Dsu{
    
    
	int * father;
	int size;
}Dsu;

Dsu * createDsu(int size){
    
    //创建并查集
	Dsu * dsu = (Dsu*)malloc(sizeof(Dsu));
	
	dsu->size = size;
	dsu->father = (int*)malloc(sizeof(int) * (size + 1));
	
	for(int i = 1;i <= size;i++){
    
    
		dsu->father[i] = i;
	}
	
	return dsu;
}


int find(Dsu * dsu,int k){
    
    
	if(k > dsu->size || k <= 0){
    
    
		return 0;
	}
	
	if(dsu->father[k] == k){
    
    
		return k;
	}
	
	dsu->father[k] = find(dsu,dsu->father[k]);
	return dsu->father[k];
}

void merge(Dsu * dsu,int x,int y){
    
    
	dsu->father[find(x)]] = find(y);
}

int edge[100050][2];//边集合
int idx[100050];
int main(){
    
    
	int n,m;
	scanf("%d%d",&n,&m);	//读入nm
	for(int i = 0;i < m;i++){
    
    //读入所有边
		scanf("%d%d",&edge[i][0],&edge[i][1]);
	}
	for(int i = 1;i <= m;i++){
    
    //读入删边次序
		scanf("%d",&idx[i]);
	}
	
	Dsu * dsu = createDsu(n);

	int cnt = n;	
	for(int i = m,x,y;i > 0;i--){
    
    //逆序加边
		x = edge[idx[i]][0];
		y = edge[idx[i]][1];
		
		if(find(dsu,x) != find(dsu,y)){
    
    //如果新边两定点不在同一集合,则合并,等价类-1
			merge(dsu,x,y);
			cnt--;
		}
		if(cnt == 1){
    
    //如果等价类数量唯一,得到答案i
			printf("%d",i);
			break;
		}
	}
}

猜你喜欢

转载自blog.csdn.net/qq_45678698/article/details/120661121