算法知识点———并查集

并查集是一种用于管理元素所属集合的数据结构,实现为一个森林,其中每棵树表示一个集合,树中的节点表示对应集合中的元素。并查集支持两种操作:

合并(Union):合并两个元素所属集合(合并对应的树)

查询(Find):查询某个元素所属集合(查询对应的树的根节点),这可以用于判断两个元素是否属于同一集合。

经典用法:利用并查集检测图中是否有环。

初始parent数组可以设置为下标,表示结点的父亲结点是自己。

然后当结点1和结点2有边的时候可以设置结点2的父亲结点是结点1.结点3的父结点是结点4,那么parent3 = 4

然后如果1和3这条边想合并的时候只需要合并1的父结点和3的父结点即可,也就是1的父结点是4.这样01234都在一个集合里面,也就是说root0 == root2 表示0和2是互通的。

下面是合并x节点和y节点的过程

第一步find_root函数找到x节点的根结点或者y节点的根结点

第二步uniin函数合并x节点和y节点

并查集合并的时候容易导致树倾斜,可以采用秩优化;

每次合并都把深度较小的集合合并在深度较大的集合下面 。这种技术被称为按秩合并
路径压缩 实际上是在找完根结点之后,在递归回来的时候顺便把路径上元素的父亲指针都指向根结点。

题目2:

判断ip是否有连通性,其中连通具有传递性;

输入描述:

第一行包含两个整数n和m,表示已知的IP地址数量和连通关系数量。

接下来n行,每行包含一个字符串和一个整数,表示一个IP地址和它的编号。

接下来m行,每行包含两个整数a和b,表示IP地址对应的编号a和b之间有连通关系。

接下来一行包含一个整数q,表示需要判断连通性的IP地址数量。

接下来q行,每行包含两个字符串,表示需要判断连通性的两个 IP地址。

输出描述

对于每个需要判断连通性的IP地址对,如果它们连通,则输出"Yes",否则输出"No"。

示例1

5 3

192.168.0.1 1

192.168.0.2 2

192.168.0.3 3

192.168.0.4 4

192.168.0.5 5

1 2

2 3

4 5

3

192.168.0.1 192.168.0.2

192.168.0.2 192.168.0.3

192.168.0.3 192.168.0.4

输出:

Yes

Yes

No

cpp 复制代码
#include <iostream>
#include <unordered_map>
#include <string>
#include <vector>

using namespace std;

// 并查集类
class UnionFind {
public:
	UnionFind(int n) {
		parent.resize(n + 1);
		rank.resize(n + 1, 0);
		for (int i = 1; i <= n; ++i) {
			parent[i] = i;
		}
	}

	// 查找集合的根节点,路径压缩优化
	int find(int x) {
		if (parent[x] != x) {
			parent[x] = find(parent[x]);
		}
		return parent[x];
	}

	// 合并两个集合,按秩合并优化
	void unite(int x, int y) {
		int rootX = find(x);
		int rootY = find(y);
		if (rootX != rootY) {
			if (rank[rootX] > rank[rootY]) {
				parent[rootY] = rootX;
			}
			else if (rank[rootX] < rank[rootY]) {
				parent[rootX] = rootY;
			}
			else {
				parent[rootY] = rootX;
				rank[rootX]++;
			}
		}
	}

private:
	vector<int> parent; // 父节点数组
	vector<int> rank;   // 秩数组 记录每个集合的"秩"或"高度"。
};

int main() {
	int n, m;
	cin >> n >> m;

	unordered_map<string, int> ipMap; // IP地址到编号的映射
	string ip;
	int id;

	// 输入n个IP地址和对应的编号
	for (int i = 0; i < n; ++i) {
		cin >> ip >> id;
		ipMap[ip] = id;
	}

	// 初始化并查集
	UnionFind uf(n);

	int a, b;
	// 输入m个连通关系
	for (int i = 0; i < m; ++i) {
		cin >> a >> b;
		uf.unite(a, b); // 将a和b所属的集合合并
	}

	int q;
	cin >> q;
	string ip1, ip2;

	// 对每个查询判断是否连通
	for (int i = 0; i < q; ++i) {
		cin >> ip1 >> ip2;
		if (uf.find(ipMap[ip1]) == uf.find(ipMap[ip2])) {
			cout << "Yes" << endl;
		}
		else {
			cout << "No" << endl;
		}
	}

	return 0;
}
相关推荐
北域码匠2 小时前
冒泡排序太慢?鸡尾酒排序双向优化,原生 C# 零第三方库完整代码
数据结构·排序算法·泛型·c# 算法·鸡尾酒排序·原生 c# 开发·冒泡排序优化·嵌入式算法
kisshyshy5 小时前
从递归到迭代,一文吃透二叉树的核心知识与 JavaScript 实现
javascript·算法·代码规范
To_OC16 小时前
LC 49 字母异位词分组:想到哈希表很简单,选对 key 才是精髓
javascript·算法·leetcode
用户9385156350721 小时前
从 O(n²) 到 O(nlogn):一文读懂快速排序的“快”与“妙”
javascript·算法
To_OC1 天前
手写快排次次翻车?别死背快排模板了,这才是面试官想听的底层逻辑
javascript·算法·排序算法
饼干哥哥1 天前
Reddit VOC调研太慢?搭一个AI专家团队半小时洞察任何品类|以猫用饮水机为例
人工智能·算法·ai编程
地平线开发者1 天前
Transformer模型部署之性能优化指南
算法
地平线开发者1 天前
人在途中:从“编译失败”到“模型可落地”——CUDA 自定义算子
算法·自动驾驶
半个落月1 天前
从递归到快速排序:用 JavaScript 把分治思想讲明白
javascript·算法·面试
小月土星1 天前
JavaScript 快速排序:从 pivot、双指针到分治思想
javascript·算法·面试