Recommended resources
The freely available resources this lesson will direct you to.
Welcome back to our study of the Union-Find data structure. In our previous lesson, we established a powerful method for counting connected components in a graph. The core insight was that if two nodes u and v had different roots (find(u) != find(v)), a union operation would merge their components. We noted that if the roots were the same, the edge was "redundant," and we simply moved on.
Today, we will focus on that very moment of redundancy. We will see that detecting this condition is not just a side effect but a powerful feature that allows us to solve a classic category of graph problems. This lesson directly addresses the learning outcome: Apply Union-Find to solve redundant connection style problems. We'll discover how the simple act of checking for an existing connection before merging components provides an elegant and efficient way to detect cycles in a graph.
The Problem: What is a Redundant Connection?
Imagine a graph that is a tree. A fundamental property of a tree with N nodes is that it has exactly N-1 edges and contains no cycles. Now, what happens if we add one more edge to this tree? With N nodes and N edges, a cycle is guaranteed to form. The edge that, when added, completes this cycle is called a redundant connection.
Our goal is to identify this specific edge. For instance, in a graph representing a computer network, a redundant connection might be an extra cable that creates a loop, potentially causing broadcast storms. In algorithm problems, we are often asked to find this edge.

The core idea for solving this is a direct extension of our work from the last lesson. We process the graph by adding edges one by one. The first edge we encounter that attempts to connect two nodes already in the same connected component is our redundant edge.
The Algorithm: Finding the Cycle
The algorithm leverages the find operation to detect pre-existing paths:
- Initialize: Start with a Union-Find data structure where each of the
Nnodes is in its own set (i.e., its own parent). - Iterate Through Edges: Process each edge
(u, v)from the input list in the given order. - Check for Existing Connection: For each edge, before performing a
union, check ifuandvare already connected. We do this by finding their ultimate parents (roots):root_u = find(u)androot_v = find(v). - Detect Redundancy:
- If
root_u == root_v, it means there is already a path betweenuandv. Adding the edge(u, v)would create a second, distinct path between them, thus forming a cycle. This is our redundant connection. We stop and return this edge. - If
root_u != root_v, the nodes are in different components. Adding this edge will connect them without creating a cycle. We proceed to merge their sets by callingunion(u, v).
- If
Because problems often specify returning the last edge in the input that could be the redundant one, this ordered-processing approach works perfectly. The first edge that fails the union operation is precisely the edge that completes the cycle formed by the previously processed edges.
A Guided Walkthrough
The NeetCode video for LeetCode problem 684, "Redundant Connection," provides a fantastic explanation of this entire process. It covers the intuition, walks through an example, and then implements the solution.
Redundant Connection - Union Find - Leetcode 684 - Python
This video by NeetCode is an excellent guide to solving the Redundant Connection problem using Union-Find.
First, watch the introduction from the beginning to understand the problem statement and the key intuition. The presenter clearly explains why a graph with N nodes and N edges must have a cycle and how this relates to the idea of connected components. Next, observe the step-by-step example from this section. Pay close attention to how the parent and rank arrays are updated with each edge and, crucially, what happens when an edge connects two nodes that already share the same root. Finally, review the Python implementation from the coding part. Notice how the union function is designed to return False when a redundant connection is detected, which neatly signals the main loop to return the current edge.
Implementation and Analysis
To solidify your understanding, let's look at the logic in a textual format. The following resources from LeetCode's discuss forum and NeetCode.io provide clean explanations and implementations.
684. Redundant Connection - Solution & Explanation
This NeetCode article provides a written-down version of the video's logic, along with code in multiple languages.
Read the Disjoint Set Union section for a concise summary of the intuition and algorithm. This reinforces what you saw in the video. Examine the Python code provided. Note the structure: a union function that returns False on cycle detection and a main loop that iterates through edges, calling union and returning the edge if it returns False. Pay special attention to the Common Pitfalls section. For someone focused on practical coding and interviews, understanding these common mistakes is highly valuable.
The core of the implementation is a slight modification to the union function we've been using. Instead of just performing the merge, it also reports on its success.
A typical implementation might look like this:
def union(n1, n2):
p1, p2 = find(n1), find(n2)
# If parents are the same, they are already connected.
# Union fails, cycle detected.
if p1 == p2:
return False
# Otherwise, perform union by rank/size
if rank[p1] > rank[p2]:
parent[p2] = p1
rank[p1] += rank[p2]
else:
parent[p1] = p2
rank[p2] += rank[p1]
# Union was successful.
return True
# Main loop
for n1, n2 in edges:
if not union(n1, n2):
return [n1, n2] # This is the redundant edge
Complexity Analysis
For a graph with N nodes and N edges (as is the case in the "Redundant Connection" problem):
- Time Complexity: O(N * α(N)). We initialize the DSU in O(N) time. Then, we iterate through
Nedges. Each iteration involves aunionoperation, which internally makes twofindcalls. With path compression and union by rank/size, these operations have an amortized complexity of O(α(N)). Since α(N) is nearly constant, the total time complexity is effectively linear, O(N). - Space Complexity: O(N) to store the
parentandrankarrays for theNnodes.
This is a significant improvement over a DFS-based approach, which would require O(N^2) time to solve this problem by repeatedly checking for cycles.
You can also explore another textual explanation of this concept.
Graph Series : Part 1 union find - Discuss
This LeetCode discuss post also explains cycle detection using Union-Find.
Focus on the section titled Cycle Detection. It provides another clear, step-by-step example of how adding edges eventually leads to discovering a redundant connection.
Conclusion
In this lesson, we've unlocked a primary application of the Union-Find data structure: detecting cycles in an undirected graph. By building on the logic from our previous lesson, we saw that the "redundant" case—where two nodes are already in the same component—is exactly the signal we need to identify the edge that completes a cycle.
Key Takeaways:
- Core Pattern: To find a redundant connection in a graph that should be a tree, iterate through its edges. The first edge
(u, v)for whichfind(u)andfind(v)return the same root is the redundant edge. - Implementation: A common pattern is to have the
unionfunction return a boolean:falseif the nodes are already connected (cycle detected), andtrueif they were successfully merged. - Efficiency: This Union-Find approach solves the problem in nearly linear time, O(N * α(N)), making it highly efficient for interview settings.
In our next lesson, we'll continue to explore the versatility of Union-Find. We will move away from explicit graph structures and apply the same concepts of grouping and merging to solve problems involving entity consolidation, such as merging duplicate user accounts based on shared information.