并查集
并查集
介绍
https://blog.csdn.net/qq_41593380/article/details/81146850
朋友圈
冗余连接
class unionfind {
public:
vector<int>father;
unionfind(const int &num) // num表示元素的个数
{
for(int i = 0; i < num; i++) {
father.push_back(i); // 箭头指向自己
}
}
int find(const int &x)
{
if (father[x] == x)
return father[x];
father[x] = find(father[x]);//路径压缩,让同集合指向一个元素
return father[x];
}
bool myunion(const int &a, const int &b) {
int fa = find(a);
int fb = find(b);
bool res = (fa == fb);
father[fb] = fa;
return res;
}
};
class Solution {
public:
vector<int> findRedundantConnection(vector<vector<int>>& edges) {
int n = edges.size();
vector<int>ans(2,0);
unionfind F(n+1);
for (int i = 0; i < n; i++) {
int a = edges[i][0];
int b = edges[i][1];
bool isok = F.myunion(a, b);
if (isok) { // 如果这两个点已经是连起来那就更新答案,因为题目要求是最后一个;
ans[0] = a;
ans[1] = b;
}
}
return ans;
}
};
以图判树
// 补充
无向图中连通分量的数目
按字典序排列最小的等效字符串
class UF {
private:
unordered_map<char, char>m_parent;
public:
UF(string s)
{
for (auto c : s)
m_parent[c] = c;
}
char GetRoot(char c)
{
if (m_parent[c] == c) return c;
m_parent[c] = GetRoot(m_parent[c]);
return m_parent[c];
}
void Union(char a, char b)
{
char pa = GetRoot(a);
char pb = GetRoot(b);
if (pa <= pb)
m_parent[pb] = pa; // 这里一定是把根的根改了,如果是只是m_parent[b] = pa;则只改了当前的节点,会有问题。
else
m_parent[pa] = pb;
}
// 调试用
void show()
{
for (auto a : m_parent) {
cout << a.first << "----" <<a.second << endl;;
}
}
};
class Solution {
public:
string smallestEquivalentString(string A, string B, string S) {
string abs = A + B + S; // 这里要把S也加进去,防止S中的AB中没有
UF uf(abs);
for (int i = 0; i < A.length(); i++) {
uf.Union(A[i], B[i]);
}
// uf.show(); // 调试用
string ret;
for (int i = 0; i < S.length(); i++) {
char tmp = uf.GetRoot(S[i]);
ret += tmp;
}
return ret;
}
};
句子相似性
class unionfind {
public:
unionfind(vector<vector<string>>& pairs)
{
for (int i = 0; i < pairs.size(); i++) {
m_map[pairs[i][0]] = pairs[i][0];
m_map[pairs[i][1]] = pairs[i][1];
}
}
string getRoot(string str)
{
if (m_map[str] == str)
return m_map[str];
m_map[str] = getRoot(m_map[str]); // 路径压缩
return m_map[str];
}
bool myunion(const string &a, const string &b)
{
string fa = getRoot(a);
string fb = getRoot(b);
bool ret = fa == fb;
m_map[fa] = fb;
return ret;
}
unordered_map<string, string>m_map; // value为root节点
};
class Solution {
public:
bool areSentencesSimilarTwo(vector<string>& words1, vector<string>& words2, vector<vector<string>>& pairs) {
if (words1.size()!= words2.size()) {
return false;
}
unionfind uf(pairs);
for (int i = 0; i < pairs.size(); i++) {
uf.myunion(pairs[i][0], pairs[i][1]);
}
for (int i=0; i < words1.size();i++) {
if (uf.getRoot(words1[i]) != uf.getRoot(words2[i]))
return false;
}
return true;
}
};
岛屿数量
class UF {
public:
vector<int> roots; // 不能是vector<char>否则,测试用例超过127后,
// 会反转到-1,在Union中,会出现数组下标索引为负数的错误
UF(int n)
{
roots.resize(n);
for (int i = 0; i < n; i++) {
roots[i] = i;
}
}
int FindRoot(int x)
{
if (roots[x] == x)
return x;
x = roots[x];
return FindRoot(x);
}
bool Union(int a, int b)
{
int ra = FindRoot(a);
int rb = FindRoot(b);
if (ra == rb)
return true;
if (ra < rb) {
roots[rb] = ra;
} else {
roots[ra] = rb;
}
return false;
}
};
int dx[4] = {-1, 0, 1, 0};
int dy[4] = {0, 1, 0, -1};
class Solution {
public:
int numIslands(vector<vector<char>>& grid) {
int rows = grid.size();
int cols = 0;
if (rows == 0) {
cols = 0;
} else {
cols = grid[0].size();
}
UF uf(rows * cols);
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
if (grid[i][j] == '1') {
for (int k = 0; k < 4; k++) {
int a = i + dx[k];
int b = j + dy[k];
if (a >= 0 && a < rows && b >= 0 && b <cols && grid[a][b] == '1') {
uf.Union(i*cols + j, a*cols + b); // 这里将二维数组下标对应到一位数组中
}
}
}
}
}
int cnt = 0;
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
if ((grid[i][j] == '1') && (uf.FindRoot(i*cols + j) == (i*cols + j)))
cnt++;
}
}
return cnt;
}
};
最低成本联通所有城市
题解
最小生成树问题
解法1:kruskal算法
kruskal算法核心思想:
1、初始阶段,每个点互不认识,各自为一个孤岛;
2、以题给定的“边“入手,不断的通过整合边,让所有的孤岛连起来;
3、利用贪心算法,从最小cost的边开始,遍历所有的边;
4、遍历过程中,如果发现当前编所在的两个点再两个孤岛上,则合并他们。这一步采用并查集方法(即为不同的集合寻找father,father相同的节点,为同一个集合);
class Solution {
public:
int find(int x) //不断向上找,直到找到下标和元素相同的点;不理解背后原因的,可以学习下树的数组表示发。
{
while(x != p[x]) x = p[x];
return x;
}
int minimumCost(int N, vector<vector<int>>& connections) {
int ans = 0;
int point_num = 0;
//sort重定义比较是重载的<号,为真则不交换,否则交换。
//基于上述原则,对cmp返回对应的true or false
auto cmp = [](vector<int> &a, vector<int> &b){return a[2] < b[2];};
for(int i = 0; i <= N; i++) {
p.push_back(i);
}
sort(connections.begin(), connections.end(), cmp);//按cost值排序,采用贪心算法
for (auto conn : connections) {
int px = find(conn[0]),py = find(conn[1]);
if(px != py) { //如果该边所在的两个节点不在同一个集合
p[px] = py; //合并集合
ans += conn[2];
point_num++;
//对于无向图的话,至少需要n-1条边可以使得图是联通的;
//如果对于有向图的话,至少需要n条边才可以使得图是联通的
if(point_num == N - 1){
return ans;
}
}
}
return -1;
}
private:
vector<int> p; //集合关系表,用一个数组来描述N个节点的集合关系;等同于树的数组表示方法。
};
kruskal就是贪心加并查集,非常的模板化。

浙公网安备 33010602011771号