目录
一并查集原理
二并查集实现
三并查集应用
省份数量
等式方程的可满意性
一并查集原理
在一些应用问题中,必要将n个不同的元素划分成不相交的集合; 开始时,每个元素自成一个单元素集合,然后按肯定的规律将不同集合举行合并; 在此过程中要查询某一个元素归属于那个集合的运算。这类问题可以用并查集来办理
好比:某公司今年校招全国总共招生5人,西安招2人,成都招3人;5个人来自不同的学校,起先互不相识,每个学生都是一个独立的小团体;
现给这些学生举行编号:{0, 1, 2, 3,4}; (-1代表在该集合中只有自己一人)
毕业后,学生们要去公司上班,每个地方的学生自发组织成小分队一起上路,于是:
西安学生小分队s1={0,1},成都学生小分队s2={2,3,4},各自形成一个小团体:
每棵树的根/每个集合的第一个数存的是集合的个数(为了与编号区别开存的是相反数),而在集合内的别的数则存的是第一个数的编号(通常是下标)
通过平常的相处,2号与1号渐渐熟悉成为了朋友,此时这两个集合就可以合并成一起,成为一个新的集合:(哪个集合合并哪个集合随意,没规定)
总结:
1. 数组的下标对应集合中元素的编号;
2. 数组的值中如果为负数,负号代表根,数字代表该集合中元素个数;
3. 数组的值中如果为非负数,代表该元素双亲在数组中的下标(编号)
二并查集实现
创建一个并查集对象通常必要根据传过来的数据个数举行开发空间并初始化成-1;实现时可以用vector来接收;(如果传过来是别的数据,我们就要在内部对数据举行编号)
并查集实现一般实现四个功能接口:
1找根;2合并两个集合;3判断是否在同一个集合;4.当前集合的个数
- #include <iostream>
- #include <vector>
- #include <unordered_map>
- using namespace std;
- // 如果是其它数据的话要自己建立映射
- // template <class T>
- // class UnionFindSet
- // {
- // public:
- // UnionFindSet(const vector<T> &v)
- // {
- // for (int i = 0; i < v.size(); i++)
- // {
- // _v.push_back(v[i]);
- // _ufs[v[i]] = i;
- // }
- // }
- // private:
- // vector<T> _v; // 值映射下标
- // unordered_map<T, int> _ufs; // 下标找值
- // };
- template <class T>
- class UnionFindSet
- {
- public:
- UnionFindSet(int size)
- {
- _ufs.resize(size, -1);
- }
- // 找根
- int FindSet(int x)
- {
- int root = x;
- while (_ufs[root] >= 0)
- {
- root = _ufs[root]; // 更新根,一定注意不要写错
- }
- return root;
- }
- // 合并集合
- void UnionSet(int x, int y)
- {
- int root1 = FindSet(x);
- int root2 = FindSet(y);
- if (root1 != root2)
- {
- _ufs[root1] += _ufs[root2];
- _ufs[root2] = root1;
- }
- }
- // 是否在同一集合
- bool IsSet(int x, int y)
- {
- return FindSet(x) == FindSet(y);
- }
- // 当前集合个数
- int Size()
- {
- int n = 0;
- for (auto e : _ufs)
- {
- if (e < 0) // n-=e;
- ++n;
- }
- return n;
- }
- private:
- vector<T> _ufs;
- };
复制代码 三并查集应用
省份数量
有了上面实现,办理该题不是轻而易举吗!
- #include <iostream>
- #include <vector>
- using namespace std;
- template <class T>
- class UnionFindSet
- {
- public:
- UnionFindSet(int size)
- {
- _ufs.resize(size, -1);
- }
- int FindSet(int x)
- {
- int root = x;
- while (_ufs[root] >= 0)
- {
- root = _ufs[root];
- }
- return root;
- }
- void UnionSet(int x, int y)
- {
- int root1 = FindSet(x);
- int root2 = FindSet(y);
- if (root1 != root2)
- {
- _ufs[root1] += _ufs[root2];
- _ufs[root2] = root1;
- }
- }
- bool IsSet(int x, int y)
- {
- return FindSet(x) == FindSet(y);
- }
- int Size()
- {
- int n = 0;
- for (auto e : _ufs)
- {
- if (e < 0) // n-=e;
- ++n;
- }
- return n;
- }
- private:
- vector<T> _ufs;
- };
- class Solution
- {
- public:
- int findCircleNum(vector<vector<int>> &isConnected)
- {
- UnionFindSet<int> ufs(isConnected.size());
- for (int i = 0; i < isConnected.size(); i++)
- {
- for (int j = 0; j < isConnected[i].size(); j++)
- {
- if (isConnected[i][j] == 1)
- {
- ufs.UnionSet(i, j);
- }
- }
- }
- return ufs.Size();
- }
- };
复制代码 等式方程的可满意性
先遍历一遍:相等的字符存在同一个集合中;
再遍历一遍:两个不相等的字符去并查集找是否在同一个集合中,是返回false;
留意:办理该问题并不愿定每次都要把并查集实现一遍:实现出我们想要的函数即可
- class Solution
- {
- public:
- bool equationsPossible(vector<string> &equations)
- {
- vector<int> ufs(26, -1);
- auto Find = [&ufs](int x)
- {
- while (ufs[x] >= 0)
- x = ufs[x]; // 注意这里的逻辑!
- return x;
- };
- for (auto &equation : equations)
- {
- if (equation[1] == '=')
- {
- int root1 = Find(equation[0] - 'a'), root2 = Find(equation[3] - 'a');
- if (root1 != root2)
- {
- ufs[root1] += ufs[root2];
- ufs[root2] = root1; // 这里也要赋值对
- }
- }
- }
- for (auto &equation : equations)
- {
- if (equation[1] == '!')
- {
- int x = Find(equation[0] - 'a');
- int y = Find(equation[3] - 'a');
- if (x == y)
- return false;
- }
- }
- return true;
- }
- };
复制代码 以上便是全部内容,有问题欢迎在评论区指正,感谢观看!
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |