首页 > 其他分享 >并查集

并查集

时间:2024-04-06 20:35:36浏览次数:22  
标签:parent int 查集 Rank 集合 节点

并查集的作用

  • 检查图中是否存在环

并查集的流程

  • 设定一个集合,叫并查集
  • 往集合里面添加边,怎么添加呢?取边的起点和终点,判断两点是否都在集合里面。如果都在,则出现了环,如果不在,则将两个点放入集合中。
  • 继续添加下一条边,直到没有边。如果最后都没有找到环,就是图中不存在环。

并查集的构造

并查集构造的三个动机:

  • 能够表示点加入集合的不同状态;
  • 方便查找点是否存在于集合中;
  • 方便两个不同的集合进行合并。

根数组:

为了满足上述三点,于是便有人想出了并查集算法,想出了用根数组:parent实现并查集。

  • parent[i]:表示第i个点的父节点。
  • parent的初始化:parent[i] = i; 或 parent[i] = -1;

表示点加入集合的不同状态:

  • 根数组用树的结构去表示点的状态。为什么用树呢?因为并查集算法就是为了检查环的存在,所以一旦有环的存在就会被判定为异常,即并查集无需表示环,而无环的连通图就是树。
  • 有了数组parent[i],就能根据父节点构造出森林出来,位于同一棵树的点自然属于同一集合。

查找点是否存在于集合:

  • 并查集算法用根代表某一个集合。如果两个点的根一样,则表明两个点处于同一棵树上,即两个点同处于它们的根所代表的集合中。
  • 而查找根的方法我们可以轻易根据数组parent实现,只需要一层一层的用父节点往上循环,直到根节点。
  • 那么,如何判断是否为根节点呢?因为根节点从未加入其他节点,所以根据初始化条件的不同,根节点的 parent[i] = i; 或 parent[i] = -1; 这就是初始化的目的。

集合的合并:

  • 既然,我们根据树和根节点来作为集合的判断依据,那么,如果我们要合并集合a和集合b,其实就是合并树a和树b。所以我们只需要将树a的根指向树b的根,或者将树b的根指向树a就可以了。

代码实现:

#include <iostream>
#include <cstring>

#define MAXL 10 // 定义根数组大小

using namespace std;

int parent[MAXL];

int Find(int x) {
	int t = x;
	while (parent[t] != -1) {
		t = parent[t]; // 一直向上递归找到根节点
	}
	return t;
}

int Join(int x, int y) {
	int parent_x = Find(x);
	int parent_y = Find(y);
	if (parent_x != parent_y) {
		parent[parent_x] = parent_y; // parent[parent_y] = parent_x也可
		return 1; // 表示无环,经合并到一个集合
	}
	return 0; // 表示有环
}

int main() {
	memset(parent, -1, sizeof(parent)); // 初始化所有节点的parent为-1

	int edge[7][2] = { {0, 1}, {1, 2}, {2, 3}, {4, 5}, {5, 6}, {1, 4}, {1, 6} };

	bool flag = true;

	for (int i = 0; i < 7; i++) { // 将每一条边放进集合
		int x = edge[i][0];
		int y = edge[i][1];
		if (Join(x, y) == 0) {
			cout << "有环" << endl;
			flag = false;
			break; // 直接退出
		}
	}
	
	if (flag) {
		cout << "无环" << endl;
	}

	return 0;
}

按秩合并

在集合合并的时候,在极端情况下会出现 0-1 1-2 2-3 3-4…… 这样一直让树的深度增加的情况。

这种情况就会导致点在查找根的时候,时间复杂度的增加。所以,为了降低算法的时间复杂度,有人提出了压缩路径和按秩合并的思想。

  • 秩:这里指树的深度。算法使用 rank 数组来记录树的深度,如 rank[x] = y 表示 以 x 点为根结点的树的深度为 y。
  • 算法未开始时,此时所有的树只有一个点,没有边,所以每个点的深度为 0,所以rank数组初始化为全0
  • 算法开始合并时,比较要合并的两棵树的深度。
  • 当两棵树的深度不一致时,让低的树的根指向高的树的根,这样新合并的树的高度就等于之前高的树的深度,而不会再度增加。
  • 当两棵树的深度一致时,随便让一棵树的根指向另一棵树的根,这样新合并的树的高度就等于之前树的深度加上1,而不会增加很多。

代码实现:

#include <iostream>
#include <cstring>

#define MAXL 10 // 定义根数组大小

using namespace std;

int parent[MAXL];
int Rank[MAXL];

int Find(int x) {
	int t = x;
	while (parent[t] != -1) {
		t = parent[t]; // 一直向上递归找到根节点
	}
	return t;
}

int Join(int x, int y) { // 合并的时候进行秩的变化
	int parent_x = Find(x);
	int parent_y = Find(y);
	if (parent_x == parent_y) {
		return 0; // 表示有环,经合并到一个集合
	}
	else if (Rank[parent_x] > Rank[parent_y]) {
		parent[parent_y] = parent_x; // 让低秩的父节点指向高秩的节点,这样合并后秩不会增加
	}
	else if (Rank[parent_x] < Rank[parent_y]) {
		parent[parent_x] = parent_y; // 让低秩的父节点指向高秩的节点,这样合并后秩不会增加
	}
	else {
		parent[parent_x] = parent_y; // 两边秩一样,可任选一个做父节点,但要将秩+1
		Rank[parent_y]++; // 如果是 parent[parent_y] = parent_x;则 Rank[parent_x]++;
	}
	return 1; // 表示无环
}

int main() {
	memset(parent, -1, sizeof(parent)); // 初始化所有节点的parent为-1
	memset(Rank, 0, sizeof(Rank));

	int edge[7][2] = { {0, 1}, {1, 2}, {2, 3}, {4, 5}, {5, 6}, {1, 4}, {1, 6} };

	bool flag = true;

	for (int i = 0; i < 7; i++) { // 将每一条边放进集合
		int x = edge[i][0];
		int y = edge[i][1];
		if (Join(x, y) == 0) {
			cout << "有环" << endl;
			flag = false;
			break; // 直接退出
		}
	}
	
	if (flag) {
		cout << "无环" << endl;
	}

	return 0;
}

 

标签:parent,int,查集,Rank,集合,节点
From: https://www.cnblogs.com/love-9/p/18117879

相关文章

  • 代码随想录 | 图论 797. 所有可能的路径(dfs) ,200. 岛屿数量 (dfs)200. 岛屿数量 (bfs)
    797.所有可能的路径https://leetcode.cn/problems/all-paths-from-source-to-target/description/List<List<Integer>>res;List<Integer>path;publicList<List<Integer>>allPathsSourceTarget(int[][]graph){res=newA......
  • 【算法】BFS、并查集和拓扑排序
    最近刷到了两道关于图论很有意思的题目()。做法颇有相似之处,因此记录一下吧AcWing2069.网络分析标签:dfs、并查集题目描述题目大意是,有一个\(n\)个顶点的网络,初始状态图中没有边。有两种操作:操作1将节点\(a\)和节点\(b\)连接起来;操作2使节点\(p\)的值加\(t\),\(t\)值会沿着网......
  • Luogu P8710 [蓝桥杯 2020 省 AB1] 网络分析 题解 [ 绿 ] [ 带权并查集 ]
    原题分析本题由于从一个节点发信息,同一个集合内的所有点都会收到信息,显然是一道要求维护各节点间关系的题,因此采用并查集的数据结构进行求解。但由于维护关系的同时还要维护权值,所以采用带权并查集,它是一种能维护某个节点与其祖宗节点之间关系的数据结构。带权并查集找父亲的......
  • 并查集(小白)
    许久未更新今天小小的学习一下 让我开始我们的学习之旅 并查集引入首先我们引入一个概念,并查集是管理元素所属集合的数据结构,可以理解为一个集合一棵树---这一颗树的每个节点都是一个元素。比如图中的元素1,2,3,4,5属于一个集合,元素6,7,8属于一个集合并查......
  • 算法题:经商(并查集+01背包)
    链接:经商来源:牛客网题目描述小d是一个搞房地产的土豪。每个人经商都有每个人经商的手段,当然人际关系是需要放在首位的。小d每一个月都需要列出来一个人际关系表,表示他们搞房地产的人的一个人际关系网,但是他的精力有限,对应他只能和能够接触到的人交际。比如1认识2,2认识3,那......
  • leetcode128. 最长连续序列【三种方法; 并查集; hashtable】
    文章目录1O(nlo......
  • 蓝桥杯T5合根植物——并查集模板题
    5.合根植物-蓝桥云课(lanqiao.cn) #include<bits/stdc++.h>usingnamespacestd;intm,n,pre[1000000];set<int>s;intfind(intx){if(pre[x]==x)returnx;returnfind(pre[x]);}intmain(){//请在此输入您的代码cin>>m>>......
  • 并查集模板
    目录并查集的存储结构并查集查询路径压缩并查集合并合并优化--启发式优化合并优化--按秩合并可撤销并查集算法原理重要操作实现并查集是一种巧妙优雅的数据结构,主要用于解决元素分组和不相交集合的合并和查询问题。并查集是大量的树(单个节点也算是树)经过合并生成一......
  • 逆序并查集
    以L2-013红色警报为例原题链接https://pintia.cn/problem-sets/994805046380707840/exam/problems/994805063963230208?type=7&page=1下面贴上代码#include<iostream>usingnamespacestd;constintN=510;intp[N],g[N][N];intfind(intx){if(x!=p......
  • 【LeetCode】LeetCode 547. 省份数量(Java版 什么是并查集)
      ......