算法 - 图论06(Swift版本)

14 阅读2分钟

并查集的应用

题目1:684. 冗余连接

讲解

属于常规并查集的应用, 依次加入每条边
如果发现某条边的两个节点已经在一个集合当中,那么就是冗余的边了。

class Solution {
    func findRedundantConnection(_ edges: [[Int]]) -> [Int] {
        var parent = Array(repeating: 0, count: edges.count + 1)
        for i in 0...edges.count {
            parent[i] = i
        }
        func findRoot(_ u: Int) -> Int {
            if u == parent[u] { return u }
            parent[u] = findRoot(parent[u])
            return parent[u]
        }
        func join(_ v: Int, _ u: Int) {
            let v = findRoot(v)
            let u = findRoot(u)
            if u == v { return }
            parent[u] = v
        }
        func isSame(_ v: Int, _ u: Int) -> Bool {
            findRoot(u) == findRoot(v)
        }
        for edge in edges {
            if isSame(edge[0], edge[1]) { 
                return edge 
            } else {
                join(edge[0], edge[1])
            }
        }
        return []
    }
}

题目2:685. 冗余连接 II

讲解

有向树 相较于 无向树, 就是构成环的边不能无脑删除。
需要配合入度的检查。
而入度检查的删除也并不是随便删一个就可以, 需要判断删哪条可行,依次遍历并且检查就好。

比如这个case[[1, 3], [2, 3], [1, 2]] , 无论是什么顺序都不能删除 [1, 2],删除其他两条中任意一条都可以。
但是如果不考虑入度,直接按照冗余连接1的做法,答案对不对就看输入的顺序了, 因为他始终会删除最后一个元素。

class Solution {
    func findRedundantDirectedConnection(_ edges: [[Int]]) -> [Int] {
        var parent = [Int]()
        func initParent() {
            parent = Array(repeating: 0, count: edges.count + 1)
            for i in 1...edges.count {
                parent[i] = i
            }
        }
        func findRoot(_ u: Int) -> Int {
            if u == parent[u] { return u }
            parent[u] = findRoot(parent[u]) 
            return parent[u]
        }
        func isSame(_ u: Int, _ v: Int) -> Bool {
            findRoot(u) == findRoot(v)
        }
        func join(_ u: Int, _ v: Int) {
            let u = findRoot(u)
            let v = findRoot(v)
            if u == v { return }
            parent[v] = u
        }
        var digree = Array(repeating: 0, count: edges.count + 1)
        // 统计入度
        edges.forEach { digree[$0[1]] += 1 }
        // 尝试删除导致入度为2的边
        func delete2DigreeEdge(edgeIndex: Int) -> Bool {
            initParent()
            for (i, edge) in edges.enumerated() {
                if i == edgeIndex { continue }
                if isSame(edge[0], edge[1]) { return false }
                join(edge[0], edge[1])
            }
            return true
        }
        
        for i in (0..<edges.count).reversed() {
            if digree[edges[i][1]] == 2, delete2DigreeEdge(edgeIndex: i) {
                return edges[i]
            }
        }
        // 没有入度为2的点,删除构成环的边。
        initParent()
        for (i, edge) in edges.enumerated() {
            if isSame(edge[0], edge[1]) { return edge }
            join(edge[0], edge[1])
        }
        return []
    }
}