An interactive visualization of the Union-Find approach to detecting the redundant edge in an undirected graph. Watch how each edge is processed and how cycle detection identifies the extra connection.
Union-Find LeetCode 684 โ Mediumn nodes labeled from 1 to n,
with one additional edge added. The added edge connects two vertices that are already in the tree,
creating a cycle.n nodes.
If there are multiple answers, return the answer that occurs last in the input.
fun findRedundantConnection(edges: Array<IntArray>): IntArray { val parent = IntArray(edges.size + 1) { it } val rank = IntArray(edges.size + 1) fun find(x: Int): Int { var x = x; while (parent[x] != x) { parent[x] = parent[parent[x]] x = parent[x] } return x } fun union(x: Int, y: Int): Boolean { var px = find(x); var py = find(y) if (px == py) { return false } // cycle! if (rank[px] < rank[py]) { val tmp = px; px = py; py = tmp } parent[py] = px if (rank[px] == rank[py]) { rank[px]++ } return true } for (edge in edges) { if (!union(edge[0], edge[1])) { return edge } } }
A tree with n nodes has exactly n - 1 edges. When we add one more edge,
it must connect two nodes that are already connected, creating a cycle. Union-Find
detects this instantly: if find(u) == find(v) before we union them, then u
and v are already in the same connected component, and the edge [u, v] is redundant.
parent[i] = i) and all ranks are 0.[u, v], try to union the two nodes:find(u) and find(v) to get their root representatives.[u, v].find() flattens the tree for near-constant lookup time.
We process each of the n edges once. Each find and union
operation takes amortized O(ฮฑ(n)) time, where ฮฑ is the inverse Ackermann
function โ effectively constant for all practical input sizes. The parent and rank arrays use
O(n) space.