实现功能:合并两个集合 查询两个元素是否在一个集合中 时间复杂度近乎O(1)
基本原理:用树的形式维护所有集合:
//
根结点的编号即–> 当前集合的编号
每个节点的p(x)
存储的是x的父节点
问题1:如何判断是否是树根
问题2:如何求x的集合编号 *
循环寻找根节点
1 while (p[x] != x) x = p[x];
问题3:如何合并两个集合?
加边
例如:px是x的集合编号,py是y的集合编号。p[x] = y (默设 x是根节点
直接将x所在的集合插到y那儿去。
优化:针对问题2*
路径压缩 :遍历一次后,将路径上的节点直接指向根节点
p[x] = root
还有个按秩压缩,不常用
代码实现:acwing 836.合并集合
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 #include <iostream> #include <vector> using namespace std;const int N = 1e5 +10 ;int p[N];int find (int x) { if (p[x] != x) p[x] = find (p[x]); return p[x]; } int main () { int n, m; cin >> n >> m; for (int i = 1 ; i <= n; ++ i) { p[i] = i; } while (m --) { char op[2 ]; int a, b; cin >> op >> a >> b; if (op[0 ] == 'M' ) p[find (a)] = find (b); else { if (find (a) == find (b)) puts ("Yes" ); else puts ("No" ); } } return 0 ; }
find
函数中的判断语句是灵活的,根据具体情况随机应变。
1 2 3 4 int find (int x) { if (p[x] != x) p[x] = find (p[x]); return p[x]; }
注意与下题中的find函数进行比较
例题:128. 最长连续序列
问题描述:给定一个未排序的整数数组 nums
,找出数字连续 的最长序列(不要求序列元素在原数组中连续)的长度。
请你设计并实现时间复杂度为 O(n)
的算法解决此问题。
示例1:1 2 3 输入:nums = [100,4,200,1,3,2] 输出:4 解释:最长数字连续序列是 [1, 2, 3, 4] 。它的长度为 4 。
代码实现:1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 class Solution {public : unordered_map<int , int > p; int find (int x) { if (p.count (x)) return p[x] = find (p[x]); else return x; } int longestConsecutive (vector<int >& nums) { for (auto x : nums) { p[x] = x + 1 ; } int ans = 0 ; for (auto x : nums) { int tmp = find (x); ans = max (ans, tmp - x); } return ans; } };
这里之所以用哈希表,是要利用unordered_map::count函数,要判断查找的数字节点是否在数组中存在。
📔博文图谱
提及本博文的链接