Hi👋SpeedAlgo • Deliberate Practice & Cognitive Ergonomics for Software Engineers

An interactive algorithm mastery and technical interview preparation platform published by Hi👋WebEnterprise. Built for senior and staff software engineers preparing for rigorous coding screens at top tech companies (FAANG/MAMAA).

13 Core Algorithmic Patterns & 225 Practice Problems

  • 1. Two Pointers (16 Paradigms, 43 Problems): Opposite ends, sorted pair sums, palindrome verification, trapping rain water, 3Sum, container with most water — includes the Sliding Window and Fast & Slow Pointers paradigms (contiguous subarray invariants, longest substrings, Floyd's cycle detection, linked list middle).
  • 2. Binary Search (9 Paradigms, 14 Problems): Monotonic predicate partitioning (feasible(x)), boundary search, rotated sorted arrays, square roots, minimum ship capacity, median of two sorted arrays, matrix median on value range.
  • 3. Bit Manipulation (5 Paradigms, 11 Problems): Counting set bits, XOR cancellation, bit reversal, bitmask pairing, maximum XOR with a bitwise trie.
  • 4. Math & Geometry (8 Paradigms, 14 Problems): Sieve of Eratosthenes, integer/roman conversions, modular arithmetic, geometric simulation.
  • 5. Tree/Graph Depth-First Search (15 Paradigms, 24 Problems): Path sums, lowest common ancestor, tree diameter, validating BSTs, tree DP (House Robber III, Binary Tree Cameras).
  • 6. Tree/Graph Breadth-First Search (5 Paradigms, 12 Problems): Level-order traversals, shortest path in unweighted graphs, zig-zag traversals, rotting oranges, word ladders.
  • 7. Graphs (12 Paradigms, 22 Problems): Topological sort, cycle detection, Dijkstra's algorithm, bipartite graph validation, network delay.
  • 8. Backtracking (6 Paradigms, 14 Problems): Subsets, permutations, combinations, constraint satisfaction, pruning, N-Queens, Sudoku solver.
  • 9. Dynamic Programming (13 Paradigms, 28 Problems): Memoization vs tabulation, 0/1 knapsack, unbounded knapsack, coin change, edit distance, longest common subsequence.
  • 10. Heap / Priority Queue (9 Paradigms, 11 Problems): Running medians, top-k elements, interval scheduling, IPO, k-way merges.
  • 11. Advanced Data Structures (7 Paradigms, 17 Problems): Trie (prefix tree), Union-Find (Disjoint Set Union), LRU Cache, LFU Cache, range-sum queries with updates.
  • 12. Stack (5 Paradigms, 9 Problems): Next greater element, largest rectangle in histogram, remove k digits, decode string, min stack.
  • 13. Intervals & Greedy (2 Paradigms, 6 Problems): Merge intervals, interval scheduling, meeting rooms by sweep line, jump game.

4-Stage Deliberate Practice Framework

  1. Stage 1 (Compare & Learn): Read the pattern's mental model, loop invariant and Big-O proof, compare the pattern's template with the problem's solution side by side, and use the step-by-step debugger. The solution can also be shown in C#, Java, TypeScript, C++, Go or Rust for almost every problem.
  2. Stage 2 (Active Recall): Rebuild the solution from memory in a code editor, with an invariant checklist and optional progressive hints, and check it against a judged test suite.
  3. Stage 3 (AI Interview Coach, coming soon): A mock-interview stage still in development; for now, students go straight from Stage 2 to Stage 4.
  4. Stage 4 (Solve on Your Own): Solve a new problem on your own against a 15-minute timer; your code is graded against a test suite. Python and TypeScript run in your browser and C# runs on our server (design problems grade in Python and TypeScript).

Equipped with a spaced-repetition review hub on a fixed 7-step schedule (1 day up to 90 days), a two-pane Studio workspace, and study notes.

Pricing, Access & Commercial Terms

  • Core Curriculum: 100% Free. No credit card required.
  • Coins: New accounts get 40 free coins once, the daily claim adds +20 coins once a day, and inviting a friend earns you +25 coins when they complete their first practice step. These are the only ways to earn coins.
  • AI Coach: Each AI coach question costs 1-5 coins, priced by the server according to its size.
  • BYOK (Bring Your Own Key): AI coaching is free with your own Google Gemini, OpenAI or OpenRouter API key (BYOK).
  • Extras: Coins also unlock extras for 24 hours: a Recall hint level (1 coin), a Solve solution reveal (2), a LeetCode problem card's solutions (1) and a pattern's extra canonical problems (1).
  • Payments & Refunds: Nothing on the site is sold for money: coins can only be earned, never bought. There are no subscriptions or charges, so there is nothing to refund.
  • Platform Operator: Hi👋WebEnterprise. Support & policies at hispeedalgo.com.
Skip to main content
Hi👋SpeedAlgo

Invariant-First Algorithmic Mastery

Arena
238Items
Theory Context•Advanced Data Structures
MediumLC 684

Redundant Connection (LeetCode 684)

Target Frequency:GoogleMetaAmazon

What a redundant edge means (assume nothing): a tree with n nodes has exactly n - 1 edges and no cycles. Adding one more edge between two nodes that are already connected creates a cycle, and that extra edge is redundant.

A graph on n nodes, labeled 1 through n, started life as a tree (connected, no cycles) and then gained one extra edge, given as an array edges of n pairs [ai, bi].

Identify an edge that can be deleted so what remains is again a tree spanning all n nodes. If more than one edge would work, return whichever one appears last in edges.

Worked Examples

Example 1
Input:edges = [[1,2],[1,3],[2,3]]
Output:[2,3]
1230
Explanation: Edges 1-2 and 1-3 already connect 2 and 3, so 2-3 closes the cycle and can be removed.
Example 2
Input:edges = [[1,2],[2,3],[3,4],[1,4],[1,5]]
Output:[1,4]
123450
Explanation: After 1-2, 2-3 and 3-4, the edge 1-4 closes a cycle, so it is the one to remove.

⚖️Formal Constraints & Bounds

  • n == edges.length

  • 3 <= n <= 1000

  • edges[i].length == 2

  • 1 <= ai < bi <= edges.length

  • ai != bi

  • There are no repeated edges

  • The given graph is connected

Deep-Dive & Conceptual Insights

Core Insight

Path compression flattens the tree height during find, achieving near-constant O(alpha(N)) amortized operations.

Real-World Scenario & Production Applications

Kruskal's Minimum Spanning Tree algorithm, social network mutual friend clustering, and circuit board trace connectivity analysis.

Step-by-Step Execution Trace Table

StepEdge Processedfind(u) Rootfind(v) Rootfind(u) == find(v)?DSU ActionDisjoint Groups
0Initial State———All nodes self-rooted {1:1, 2:2, 3:3}3
1[1, 2]12NoMerge: parent[2] = 12
2[1, 3]13NoMerge: parent[3] = 11
3[2, 3]find(2) = 1find(3) = 1Yes (1 == 1)Cycle detected! Return [2, 3]1
Code Shape
Archetype Code Shape
Python
1def find(i):
2 if parent[i] != i:
3 parent[i] = find(parent[i]) # path compression
4 return parent[i]
5def union(i, j):
6 root_i, root_j = find(i), find(j)
7 if root_i == root_j:
8 return False # the edge closes a cycle
9 attach the root with the lower rank under the other root
10 return True
11return redundant edge when union returns False

Conceptual Narrative

Checking whether two nodes are already connected by running a full BFS/DFS on every new edge is O(V) per query. Disjoint Set Union (DSU) maintains dynamic connectivity in near-constant O(alpha(V)) time.

🧭 Conceptual Foundation & Pattern Intuition

A tree with NNN vertices has exactly N−1N-1N−1 edges and contains no cycles. LC 684 adds exactly one extra edge to an NNN-node tree, creating a single cycle. We are tasked with identifying the edge that induces this cycle. If we process edges sequentially, an edge (u,v)(u, v)(u,v) creates a cycle if and only if uuu and vvv are already connected through previously processed edges. Using BFS/DFS to test reachability before adding each edge would cost O(V2)O(V^2)O(V2) overall. Disjoint Set Union (Union-Find) maintains a collection of disjoint sets where each set is represented by a canonical root node. With two critical optimizations:

  1. Path Compression: find(x) first climbs to the root, then walks the path again and points every visited node straight at the root (self.parent[x] = root), flattening the tree to depth 1.
  2. Union by Size: When uniting two trees, always attach the root of the smaller tree (by size) under the root of the larger one. Together, these optimizations guarantee that any sequence of MMM operations on NNN elements executes in O(M⋅α(N))O(M \cdot \alpha(N))O(M⋅α(N)) time, where α(N)<5\alpha(N) < 5α(N)<5 for all values in the observable universe.
🏳️ The Analogy: Merging Sports Teams and Spotting a Redundant Alliance

Imagine NNN athletes competing in a tournament. Initially, each athlete is their own team captain. When an alliance is formed between player uuu and player vvv, their two captains meet and one captain agrees to report to the other. Now, whenever an athlete needs authorization, they look up to their team captain. If two athletes uuu and vvv want to form an alliance, but when they trace their captains they discover they already report to the exact same captain, that alliance is redundant: adding it would create a cycle.

🔑 Why It Works

Testing if not uf.union(a, b) answers "does this edge complete a cycle?" in amortized O(α(N))O(\alpha(N))O(α(N)) time, immediately isolating the redundant connection.

  • Omitting path compression: A find that only climbs to root and skips the second loop (self.parent[x] = root for every node on the way) leaves long chains, so later find calls can cost O(N)O(N)O(N) instead of near-O(1)O(1)O(1).

  • 1-based vertex indexing off-by-one: Graph vertices are labeled from 1 to NNN. Initializing parent array with size NNN causes IndexError when accessing parent[N]. Allocate size N+1N + 1N+1.

  • Returning arbitrary cycle edge: LC 684 requires returning the edge that occurs last in the input that creates a cycle; returning an earlier cycle edge fails the problem requirement.

Senior SWE Reasoning Architecture

4-Phase Thought Process Model

Pattern Recognition Signals

The 10-second spot

"A tree with one extra edge added" and "return an edge that can be removed so the graph is a tree": edges arrive one by one and you ask whether their ends are already connected.

Formulating the Predicate & Invariants

Turning intuition into a boolean rule

Invariant: find(a) == find(b) exactly when a and b are in the same group, so an edge with ra == rb closes a cycle.

Silent Failure Traps & Edge Cases

Where confident candidates still lose points

  • Link roots, not items: self.parent[rb] = ra after both find calls; self.parent[b] = a leaves part of b's group behind.

  • Nodes are numbered 1..n: UnionFind(len(edges) + 1), or node n is out of range.

The 60-Second Interview Pitch

Say this out loud before you type a single line

"I'd use Union-Find. Each group is named by its root: find follows parent up to the root and then points every node on the way straight at it, and union links the root of the smaller group under the root of the larger, by size. For Redundant Connection I add the edges in order. The graph was a tree plus one edge, so exactly one cycle exists, and the first edge whose two ends already share a root is the edge that closes it, which is also the last cycle edge in the input. The trap is linking the items instead of their roots, which splits a group. Each call is almost O(1) amortized, so the pass is O(N · alpha(N)) time and O(N) space."

Big-O Invariant Derivation

Complexity & Mathematical Proof

TIME COMPLEXITY

O(N · alpha(N)) for N edges, O(alpha(N)) amortized per call

Look at the code: UnionFind(len(edges) + 1) fills parent and size in O(N). The loop then calls union once per edge, at most N times, and each union makes two iterative find calls. Path compression (the second loop in find) plus union by size (size[ra] < size[rb] decides which root goes under) make each call O(alpha(N)) amortized, where alpha is the inverse Ackermann function, at most 4 for any input that fits in memory. Total: O(N + N · alpha(N)) = O(N · alpha(N)).

SPACE COMPLEXITY

O(N)

Look at memory allocations: parent and size hold N + 1 entries each, O(N). find and union are loops, so the call stack is O(1). The answer is one edge, O(1).

Formal Recurrence Relation

T(N) = O(N) + N · O(α(N)) = O(N · α(N))

Derivation Progression

Set Up

self.parent = list(range(n)), self.size = [1] * n ⟹ O(N)

Every node starts as its own group of size 1.

Path Compression Find

while x != root: self.parent[x] = root

The first loop climbs to root; the second points every node on the way straight at it, so later calls on those nodes take one step.

Union by Size

if self.size[ra] < self.size[rb]: ra, rb = rb, ra ⟹ O(α(N)) amortized

The smaller group's root goes under the larger one, so trees stay shallow; with path compression each call is O(α(N)) amortized.

One Pass Over the Edges

N · union(a, b) ⟹ O(N · α(N))

The loop stops at the first union that returns False, so it makes at most N calls.

Variable Definitions

NNN

Number of edges, len(edges); the graph also has N nodes, labeled 1..N

α(N)α(N)α(N)

Inverse Ackermann function (at most 4 for any practical N)

Memory Architecture & Bounds

🟣 Call Stack

O(1) Iterative find/union

🔵 Auxiliary Heap

O(N) parent and size lists

🟢 Output Space

O(1) One edge [a, b]

Boundary Best / Worst Cases

Best Case

O(N)O(N)O(N) when the cycle closes early (set-up still costs O(N))

Average Case

O(N⋅α(N))O(N · α(N))O(N⋅α(N))

Worst Case

O(N⋅α(N))O(N · α(N))O(N⋅α(N))

Recurrence Tree Topology

Recurrence Tree Topology
Synthesizing vector architecture diagram...
Staff+ Engineering Perspective

Senior SWE Deconstruction & Hardware Caveats

ARCHITECTURAL SIGNALS & INTERVIEW TRIGGERS

Triggers: "undirected graph that started as a tree with one additional edge added", "return an edge that can be removed so the graph is a tree". Tree with NNN vertices has exactly N−1N-1N−1 edges. An extra edge introduces a cycle. A Senior SWE immediately uses Union-Find with path compression and union by size, adding the edges in input order.

CONSTRAINTS & BOUNDS

N≤1000N \le 1000N≤1000 vertices, NNN edges. DSU executes in near-linear O(N⋅α(N))O(N \cdot \alpha(N))O(N⋅α(N)) time where α\alphaα is the Inverse Ackermann function (α(N)<5\alpha(N) < 5α(N)<5).

FAANG PRODUCTION TRAPS & EDGE CASES

Online only: the structure answers "already connected?" as edges stream in, but it cannot undo a union, so a graph that also deletes edges needs a different structure. Long chains: a recursive find can hit Python's default recursion limit before compression flattens the chain; the iterative two-loop find never does.

Core Algorithmic State Invariants

  1. The First Failing Edge Is the Answer

The graph was a tree plus one edge, so it has exactly one cycle. Adding edges in order, the only union that can fail is the one that completes that cycle, and every other cycle edge came before it, so it is the cycle edge that appears last in the input.

Theory Context•Advanced Data Structures
MediumLC 684

Redundant Connection (LeetCode 684)

Target Frequency:GoogleMetaAmazon

What a redundant edge means (assume nothing): a tree with n nodes has exactly n - 1 edges and no cycles. Adding one more edge between two nodes that are already connected creates a cycle, and that extra edge is redundant.

A graph on n nodes, labeled 1 through n, started life as a tree (connected, no cycles) and then gained one extra edge, given as an array edges of n pairs [ai, bi].

Identify an edge that can be deleted so what remains is again a tree spanning all n nodes. If more than one edge would work, return whichever one appears last in edges.

Worked Examples

Example 1
Input:edges = [[1,2],[1,3],[2,3]]
Output:[2,3]
1230
Explanation: Edges 1-2 and 1-3 already connect 2 and 3, so 2-3 closes the cycle and can be removed.
Example 2
Input:edges = [[1,2],[2,3],[3,4],[1,4],[1,5]]
Output:[1,4]
123450
Explanation: After 1-2, 2-3 and 3-4, the edge 1-4 closes a cycle, so it is the one to remove.

⚖️Formal Constraints & Bounds

  • n == edges.length

  • 3 <= n <= 1000

  • edges[i].length == 2

  • 1 <= ai < bi <= edges.length

  • ai != bi

  • There are no repeated edges

  • The given graph is connected

Deep-Dive & Conceptual Insights

Core Insight

Path compression flattens the tree height during find, achieving near-constant O(alpha(N)) amortized operations.

Real-World Scenario & Production Applications

Kruskal's Minimum Spanning Tree algorithm, social network mutual friend clustering, and circuit board trace connectivity analysis.

Step-by-Step Execution Trace Table

StepEdge Processedfind(u) Rootfind(v) Rootfind(u) == find(v)?DSU ActionDisjoint Groups
0Initial State———All nodes self-rooted {1:1, 2:2, 3:3}3
1[1, 2]12NoMerge: parent[2] = 12
2[1, 3]13NoMerge: parent[3] = 11
3[2, 3]find(2) = 1find(3) = 1Yes (1 == 1)Cycle detected! Return [2, 3]1
Code Shape
Archetype Code Shape
Python
1def find(i):
2 if parent[i] != i:
3 parent[i] = find(parent[i]) # path compression
4 return parent[i]
5def union(i, j):
6 root_i, root_j = find(i), find(j)
7 if root_i == root_j:
8 return False # the edge closes a cycle
9 attach the root with the lower rank under the other root
10 return True
11return redundant edge when union returns False

Conceptual Narrative

Checking whether two nodes are already connected by running a full BFS/DFS on every new edge is O(V) per query. Disjoint Set Union (DSU) maintains dynamic connectivity in near-constant O(alpha(V)) time.

🧭 Conceptual Foundation & Pattern Intuition

A tree with NNN vertices has exactly N−1N-1N−1 edges and contains no cycles. LC 684 adds exactly one extra edge to an NNN-node tree, creating a single cycle. We are tasked with identifying the edge that induces this cycle. If we process edges sequentially, an edge (u,v)(u, v)(u,v) creates a cycle if and only if uuu and vvv are already connected through previously processed edges. Using BFS/DFS to test reachability before adding each edge would cost O(V2)O(V^2)O(V2) overall. Disjoint Set Union (Union-Find) maintains a collection of disjoint sets where each set is represented by a canonical root node. With two critical optimizations:

  1. Path Compression: find(x) first climbs to the root, then walks the path again and points every visited node straight at the root (self.parent[x] = root), flattening the tree to depth 1.
  2. Union by Size: When uniting two trees, always attach the root of the smaller tree (by size) under the root of the larger one. Together, these optimizations guarantee that any sequence of MMM operations on NNN elements executes in O(M⋅α(N))O(M \cdot \alpha(N))O(M⋅α(N)) time, where α(N)<5\alpha(N) < 5α(N)<5 for all values in the observable universe.
🏳️ The Analogy: Merging Sports Teams and Spotting a Redundant Alliance

Imagine NNN athletes competing in a tournament. Initially, each athlete is their own team captain. When an alliance is formed between player uuu and player vvv, their two captains meet and one captain agrees to report to the other. Now, whenever an athlete needs authorization, they look up to their team captain. If two athletes uuu and vvv want to form an alliance, but when they trace their captains they discover they already report to the exact same captain, that alliance is redundant: adding it would create a cycle.

🔑 Why It Works

Testing if not uf.union(a, b) answers "does this edge complete a cycle?" in amortized O(α(N))O(\alpha(N))O(α(N)) time, immediately isolating the redundant connection.

  • Omitting path compression: A find that only climbs to root and skips the second loop (self.parent[x] = root for every node on the way) leaves long chains, so later find calls can cost O(N)O(N)O(N) instead of near-O(1)O(1)O(1).

  • 1-based vertex indexing off-by-one: Graph vertices are labeled from 1 to NNN. Initializing parent array with size NNN causes IndexError when accessing parent[N]. Allocate size N+1N + 1N+1.

  • Returning arbitrary cycle edge: LC 684 requires returning the edge that occurs last in the input that creates a cycle; returning an earlier cycle edge fails the problem requirement.

Senior SWE Reasoning Architecture

4-Phase Thought Process Model

Pattern Recognition Signals

The 10-second spot

"A tree with one extra edge added" and "return an edge that can be removed so the graph is a tree": edges arrive one by one and you ask whether their ends are already connected.

Formulating the Predicate & Invariants

Turning intuition into a boolean rule

Invariant: find(a) == find(b) exactly when a and b are in the same group, so an edge with ra == rb closes a cycle.

Silent Failure Traps & Edge Cases

Where confident candidates still lose points

  • Link roots, not items: self.parent[rb] = ra after both find calls; self.parent[b] = a leaves part of b's group behind.

  • Nodes are numbered 1..n: UnionFind(len(edges) + 1), or node n is out of range.

The 60-Second Interview Pitch

Say this out loud before you type a single line

"I'd use Union-Find. Each group is named by its root: find follows parent up to the root and then points every node on the way straight at it, and union links the root of the smaller group under the root of the larger, by size. For Redundant Connection I add the edges in order. The graph was a tree plus one edge, so exactly one cycle exists, and the first edge whose two ends already share a root is the edge that closes it, which is also the last cycle edge in the input. The trap is linking the items instead of their roots, which splits a group. Each call is almost O(1) amortized, so the pass is O(N · alpha(N)) time and O(N) space."

Big-O Invariant Derivation

Complexity & Mathematical Proof

TIME COMPLEXITY

O(N · alpha(N)) for N edges, O(alpha(N)) amortized per call

Look at the code: UnionFind(len(edges) + 1) fills parent and size in O(N). The loop then calls union once per edge, at most N times, and each union makes two iterative find calls. Path compression (the second loop in find) plus union by size (size[ra] < size[rb] decides which root goes under) make each call O(alpha(N)) amortized, where alpha is the inverse Ackermann function, at most 4 for any input that fits in memory. Total: O(N + N · alpha(N)) = O(N · alpha(N)).

SPACE COMPLEXITY

O(N)

Look at memory allocations: parent and size hold N + 1 entries each, O(N). find and union are loops, so the call stack is O(1). The answer is one edge, O(1).

Formal Recurrence Relation

T(N) = O(N) + N · O(α(N)) = O(N · α(N))

Derivation Progression

Set Up

self.parent = list(range(n)), self.size = [1] * n ⟹ O(N)

Every node starts as its own group of size 1.

Path Compression Find

while x != root: self.parent[x] = root

The first loop climbs to root; the second points every node on the way straight at it, so later calls on those nodes take one step.

Union by Size

if self.size[ra] < self.size[rb]: ra, rb = rb, ra ⟹ O(α(N)) amortized

The smaller group's root goes under the larger one, so trees stay shallow; with path compression each call is O(α(N)) amortized.

One Pass Over the Edges

N · union(a, b) ⟹ O(N · α(N))

The loop stops at the first union that returns False, so it makes at most N calls.

Variable Definitions

NNN

Number of edges, len(edges); the graph also has N nodes, labeled 1..N

α(N)α(N)α(N)

Inverse Ackermann function (at most 4 for any practical N)

Memory Architecture & Bounds

🟣 Call Stack

O(1) Iterative find/union

🔵 Auxiliary Heap

O(N) parent and size lists

🟢 Output Space

O(1) One edge [a, b]

Boundary Best / Worst Cases

Best Case

O(N)O(N)O(N) when the cycle closes early (set-up still costs O(N))

Average Case

O(N⋅α(N))O(N · α(N))O(N⋅α(N))

Worst Case

O(N⋅α(N))O(N · α(N))O(N⋅α(N))

Recurrence Tree Topology

Recurrence Tree Topology
Synthesizing vector architecture diagram...
Staff+ Engineering Perspective

Senior SWE Deconstruction & Hardware Caveats

ARCHITECTURAL SIGNALS & INTERVIEW TRIGGERS

Triggers: "undirected graph that started as a tree with one additional edge added", "return an edge that can be removed so the graph is a tree". Tree with NNN vertices has exactly N−1N-1N−1 edges. An extra edge introduces a cycle. A Senior SWE immediately uses Union-Find with path compression and union by size, adding the edges in input order.

CONSTRAINTS & BOUNDS

N≤1000N \le 1000N≤1000 vertices, NNN edges. DSU executes in near-linear O(N⋅α(N))O(N \cdot \alpha(N))O(N⋅α(N)) time where α\alphaα is the Inverse Ackermann function (α(N)<5\alpha(N) < 5α(N)<5).

FAANG PRODUCTION TRAPS & EDGE CASES

Online only: the structure answers "already connected?" as edges stream in, but it cannot undo a union, so a graph that also deletes edges needs a different structure. Long chains: a recursive find can hit Python's default recursion limit before compression flattens the chain; the iterative two-loop find never does.

Core Algorithmic State Invariants

  1. The First Failing Edge Is the Answer

The graph was a tree plus one edge, so it has exactly one cycle. Adding edges in order, the only union that can fail is the one that completes that cycle, and every other cycle edge came before it, so it is the cycle edge that appears last in the input.

© 2026 Hi👋WebEnterprise. All rights reserved.
Sitemap•llms.txt•