Medium草稿★★★★O(n * α(n)) 時間 · O(n) 空間
GraphUnion-FindDFS
Patterns🔗 並查集🕸️ 圖遍歷・連通分量
尚未複習過

解法已隱藏 — 先讀題目敘述、自己想想看,再點上方按鈕揭曉。

684Redundant ConnectionGraphsMediumGraphs

給定一個本應是樹的無向圖,多了一條邊形成環。圖以邊的列表 edges 給出(按輸入順序),找出移除後能使圖恢復為樹的那條多餘邊。若有多個答案,回傳最後出現的那條。

Example:

Input: edges = [[1,2],[1,3],[2,3]] Output: [2,3]

Intuition

TIP

依序加入邊,第一條使得兩個已連通節點再次連接的邊就是多餘的。Union-Find 完美契合。

Approaches

1. DFS (check connectivity before each edge) — O(n^2) / O(n)
  • Idea: 維護鄰接表,每加一條邊前用 DFS 檢查兩端是否已連通
  • Time: O(n^2)(每條邊做一次 DFS)
  • Space: O(n)
class Solution {
    fun findRedundantConnection(edges: Array<IntArray>): IntArray {
        val n = edges.size
        val graph = Array(n + 1) { mutableListOf<Int>() }

        fun hasPath(src: Int, dst: Int, visited: BooleanArray): Boolean {
            if (src == dst) return true
            visited[src] = true
            for (next in graph[src]) {
                if (!visited[next] && hasPath(next, dst, visited)) return true
            }
            return false
        }

        for ((u, v) in edges) {
            if (graph[u].isNotEmpty() && graph[v].isNotEmpty()) {
                val visited = BooleanArray(n + 1)
                if (hasPath(u, v, visited)) return intArrayOf(u, v)
            }
            graph[u].add(v)
            graph[v].add(u)
        }
        return intArrayOf()
    }
}
⭐ 2. Union-Find — O(n * α(n)) / O(n)
  • Idea: 依序對每條邊做 union,若兩端已在同一集合,該邊即為答案
  • Time: O(n * α(n))(近似線性)
  • Space: O(n)
class Solution {
    private lateinit var parent: IntArray
    private lateinit var rank: IntArray

    fun findRedundantConnection(edges: Array<IntArray>): IntArray {
        val n = edges.size
        parent = IntArray(n + 1) { it }
        rank = IntArray(n + 1)

        for ((u, v) in edges) {
            if (!union(u, v)) return intArrayOf(u, v)
        }
        return intArrayOf()
    }

    private fun find(x: Int): Int {
        if (parent[x] != x) parent[x] = find(parent[x])
        return parent[x]
    }

    private fun union(x: Int, y: Int): Boolean {
        val px = find(x)
        val py = find(y)
        if (px == py) return false
        if (rank[px] < rank[py]) parent[px] = py
        else if (rank[px] > rank[py]) parent[py] = px
        else { parent[py] = px; rank[px]++ }
        return true
    }
}

🔑 Takeaways