1. 程式人生 > 實用技巧 >1489. 找到最小生成樹裡的關鍵邊和偽關鍵邊 並查集

1489. 找到最小生成樹裡的關鍵邊和偽關鍵邊 並查集

給你一個 n個點的帶權無向連通圖,節點編號為 0到 n-1,同時還有一個數組 edges,其中 edges[i] = [fromi, toi, weighti]表示在fromi和toi節點之間有一條帶權無向邊。最小生成樹(MST) 是給定圖中邊的一個子集,它連線了所有節點且沒有環,而且這些邊的權值和最小。

請你找到給定圖中最小生成樹的所有關鍵邊和偽關鍵邊。如果從圖中刪去某條邊,會導致最小生成樹的權值和增加,那麼我們就說它是一條關鍵邊。偽關鍵邊則是可能會出現在某些最小生成樹中但不會出現在所有最小生成樹中的邊。

請注意,你可以分別以任意順序返回關鍵邊的下標和偽關鍵邊的下標。

來源:力扣(LeetCode)
連結:

https://leetcode-cn.com/problems/find-critical-and-pseudo-critical-edges-in-minimum-spanning-tree
著作權歸領釦網路所有。商業轉載請聯絡官方授權,非商業轉載請註明出處。

class Solution {
public:
    unordered_map <int, int> fa;
    unordered_map <int, int> rank;
    int count;

    int find(int x) {
        if (!fa.count(x)) {
            fa[x] = x;
            rank[x] = 1;
        }
        return fa[x] == x ? fa[x] : fa[x] = find(fa[x]);
    }

    bool same(int x, int y) {
        return find(x) == find(y);
    }

    bool unite(int x, int y) {
        int xx = find(x);
        int yy = find(y);
        if (same(xx, yy)) {
            return false;
        }
        if (rank[xx] < rank[yy]) {
            swap(xx, yy);
        }
        rank[xx] += rank[yy];
        fa[yy] = xx;
        count--;
        return true;
    }

    vector<vector<int>> findCriticalAndPseudoCriticalEdges(int n, vector<vector<int>>& edges) {
        vector <vector<int>> ans(2);

        int m = edges.size();
        // 記錄每條邊的id,後面輸出關鍵邊用
        for (int i = 0; i < m; i++) {
            edges[i].emplace_back(i);
        }

        // 正常kruskal求最小生成樹
        int value = 0;
        sort(edges.begin(), edges.end(), [](const auto& u, const auto& v) {
            return u[2] < v[2];
        });
        for (int i = 0; i < m; i++) {
            if (unite(edges[i][0], edges[i][1])) {
                value += edges[i][2];
            }
        }

        // 判斷是否是關鍵邊
        for (int i = 0; i < m; i++) {
            // 初始化
            fa.clear();
            rank.clear();
            count = n;
            int v = 0;
            // kruskal
            for (int j = 0; j < m; j++) {
                if (j != i && unite(edges[j][0], edges[j][1])) {
                    v += edges[j][2];
                }
            }
            if (count != 1 || (count == 1 && v > value)) {
                ans[0].emplace_back(edges[i][3]);
                continue;
            }

            // 判斷是否是偽關鍵邊
            // 初始化
            fa.clear();
            rank.clear();
            count = n;
            v = 0;
            unite(edges[i][0], edges[i][1]);
            v = edges[i][2];

            for (int j = 0; j < m; j++) {
                if (j != i && unite(edges[j][0], edges[j][1])) {
                    v += edges[j][2];
                }
            }
            if (v == value) {
                ans[1].emplace_back(edges[i][3]);
            }
        }

        return ans;
    }
};