并查集

并查集

介绍

https://blog.csdn.net/qq_41593380/article/details/81146850

朋友圈

547朋友圈

冗余连接

684冗余连接

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;
    }
};

以图判树

261以图判树

// 补充

无向图中连通分量的数目

323

按字典序排列最小的等效字符串

1061. 按字典序排列最小的等效字符串

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;
    }
};

句子相似性

737句子相似性II


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;
    }
};

岛屿数量

200岛屿数量

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;
    }
};

最低成本联通所有城市

1135 最低成本联通所有城市

题解
最小生成树问题
解法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就是贪心加并查集,非常的模板化。

尽量减少恶意软件的传播

924尽量减少恶意软件的传播

posted @ 2020-07-09 15:12  ren_zhg1992  阅读(122)  评论(0)    收藏  举报