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•Graph Algorithms
Medium

Flight Routes Check

A country has n cities, numbered 1 to n, and m one-way flights. A flight [a, b] takes you from city a to city b, and it does not take you back. A route is any sequence of flights, one after another.

Decide whether every city can reach every other city by some route. If it can, return ["YES"]. If it cannot, return ["NO", a, b], where a and b are two cities and no route leads from a to b. If several such pairs exist, any one of them is accepted.

Worked Examples

Example 1
Input:n = 3, flights = [[1,2],[2,3],[3,1]]
Output:["YES"]
1230
Explanation: The three cities form one cycle, so every city reaches every other city.
Example 2
Input:n = 4, flights = [[1,2],[2,3],[3,1],[1,4],[3,4]]
Output:["NO", 4, 3]
12340
Explanation: Cities 1, 2 and 3 can reach each other, but city 4 has no flight out. So no route leads from city 4 to any other city. Any pair that starts at city 4 is accepted, for example ["NO", 4, 1].

⚖️Formal Constraints & Bounds

  • 1 <= n <= 105

  • 1 <= m <= 2 * 105 (the number of flights)

  • 1 <= a, b <= n

Deep-Dive & Conceptual Insights

Core Insight

One depth-first search finds every group in which the cities reach each other. A city whose low equals its own disc is the first city of a group, and popping group_stack down to it collects the group. One group means every city reaches every other city. With more groups, the first group completed has no flight out, so it cannot reach any other group.

Real-World Scenario & Production Applications

Checking that a one-way network is connected in both directions: a set of one-way streets where every junction must be reachable from every other junction, or a set of services that must all be able to call each other through a chain of calls.

Step-by-Step Execution Trace Table

Step-by-Step Tarjan's Algorithm (n = 4, flights = [[1,2],[2,3],[3,1],[1,4],[3,4]])
  1. Step 1 (Start at 1): disc[1] = low[1] = 1, group_stack = [1].
  2. Step 2 (Down to 2, then 3): disc[2] = low[2] = 2 and disc[3] = low[3] = 3, so group_stack = [1, 2, 3].
  3. Step 3 (A Flight Back to a Waiting City): city 3 has a flight to city 1, and city 1 is on group_stack, so low[3] = min(3, disc[1]) = 1.
  4. Step 4 (Down to 4): city 3 also has a flight to city 4, which is new: disc[4] = low[4] = 4 and group_stack = [1, 2, 3, 4].
  5. Step 5 (City 4 Finishes): city 4 has no flight out and low[4] == disc[4], so group_stack is popped down to city 4. Group 0 is {4}, and group_stack = [1, 2, 3].
  6. Step 6 (Cities 3 and 2 Finish): low[3] = 1 is not disc[3] = 3, so city 3 stays on the stack and hands low = 1 to city 2. City 2 is also not a root, and it hands low = 1 to city 1.
  7. Step 7 (City 1 Sees City 4): city 4 was reached, but it is no longer on group_stack, so on_stack[4] is False and disc[4] is not used. City 1 finishes with low[1] == disc[1] == 1, so the stack is popped down to city 1. Group 1 is {3, 2, 1}.
  8. Step 8 (Answer): comp_id = 2, so the answer is NO. The first group completed is city 4, and no flight leaves it, so sink = 4 and other = 3. Return ["NO", 4, 3] ✅.
Full Walkthrough8 Steps
Input:n = 3, flights = [[1,2],[2,3],[3,1]]Expected:["YES"]
1⚡ STEP(Start at 1)
disc[1] = low[1] = 1, group_stack = [1].
2⚡ STEP(Down to 2, then 3)
disc[2] = low[2] = 2 and disc[3] = low[3] = 3, so group_stack = [1, 2, 3].
3⚡ STEP(A Flight Back to a Waiting City)
city 3 has a flight to city 1, and city 1 is on group_stack, so low[3] = min(3, disc[1]) = 1.
4⚡ STEP(Down to 4)
city 3 also has a flight to city 4, which is new: disc[4] = low[4] = 4 and group_stack = [1, 2, 3, 4].
5↩️ BACKTRACK / POP(City 4 Finishes)
city 4 has no flight out and low[4] == disc[4], so group_stack is popped down to city 4. Group 0 is {4}, and group_stack = [1, 2, 3].
6⚡ STEP(Cities 3 and 2 Finish)
low[3] = 1 is not disc[3] = 3, so city 3 stays on the stack and hands low = 1 to city 2. City 2 is also not a root, and it hands low = 1 to city 1.
7↩️ BACKTRACK / POP(City 1 Sees City 4)
city 4 was reached, but it is no longer on group_stack, so on_stack[4] is False and disc[4] is not used. City 1 finishes with low[1] == disc[1] == 1, so the stack is popped down to city 1. Group 1 is {3, 2, 1}.
8✅ RECORD / GOAL(Answer)
comp_id = 2, so the answer is NO. The first group completed is city 4, and no flight leaves it, so sink = 4 and other = 3. Return ["NO", 4, 3] ✅.
Code Shape
Archetype Code Shape
Python
1adj = the flights that leave each city
2disc, low, on_stack, group_stack = the search state
3for start in 1..n:
4 if start is not reached yet: search from start
5 a new city gets the next disc and waits on group_stack
6 a reached city that still waits lowers low[city] to its disc
7 a finished city hands its low to its parent
8 if low[city] == disc[city]: pop group_stack down to city as one group
9return YES for one group, else NO with a city of the first group and a city outside it

Conceptual Narrative

🧭 Conceptual Foundation & Pattern Intuition

Tarjan's algorithm finds every strongly connected group of a directed graph with one depth-first search. A group is a set of cities where each city can reach every other city of the set. In Flight Routes Check, one group that holds all the cities means that every city can reach every other city.

The search gives each city a number disc when it first reaches the city, and it puts the city on a stack called group_stack. A city on this stack is waiting: it has been reached, but its group is not complete. For each city, low is the smallest disc of a waiting city that the city's subtree can reach. When a city has tried all of its flights and low[city] == disc[city], nothing below it can climb above it. That city is the first city of a group, and the search pops group_stack down to it to collect the group.

🏨 The Analogy: A Waiting Room With Numbered Tickets

Each city takes a ticket number when the search first arrives, and it sits in a waiting room. A flight from the current city to a city that still sits in the room means that the current city can fly back to an older ticket, so its group cannot leave yet. A flight to a city that already left the room leads to a group that is complete, and it is ignored. When the current city's own ticket is the smallest number it can reach, no city behind it in the room can reach an older ticket. That city and every city that arrived after it and still waits leave the room together as one group.

🔑 The Key Idea in the Code

Two lines decide the result. The line if on_stack[nxt]: lets a reached city lower low[city] only while that city still waits, because a city in a complete group cannot lead back. The line if low[city] == disc[city]: marks the first city of a group, and the loop that pops group_stack until it reaches city collects the whole group.

Kosaraju's algorithm gets the same groups with two passes and a reversed copy of the graph. Tarjan's algorithm needs one pass over the original graph, and it keeps the extra arrays low and on_stack instead.

  • Using disc[nxt] for every reached city (the trap): low[city] = min(low[city], disc[nxt]) must sit under if on_stack[nxt]:. A city that already left group_stack is in a complete group, and using it keeps city from being popped.

  • Clearing on_stack when the search leaves a city: the flag stays True until the city is popped with its group. If it is cleared earlier, a later flight to a waiting city is ignored.

  • Not handing low up to the parent: low[parent] = min(low[parent], low[city]) comes after path.pop(). Without it, a flight back to an older city never reaches the cities in between.

  • Recursive DFS at scale: with n up to 105 cities, a recursive search passes Python's recursion limit on one long chain of flights. The search keeps its own path.

Senior SWE Reasoning Architecture

4-Phase Thought Process Model

Pattern Recognition Signals

The 10-second spot

"Whether every city can reach every other city" over "one-way flights": reaching each other in both directions is the definition of a strongly connected group. If the whole graph is one group, the answer is YES. If not, the answer needs a pair with no route. With up to 105 cities and 2 * 105 flights, a search from every city is too slow, so the answer has to come from one linear search.

Formulating the Predicate & Invariants

Turning intuition into a boolean rule

group_stack holds the reached cities whose group is not complete. low[city] is the smallest disc of a waiting city that city's subtree reaches. A city is the first city of its group exactly when low[city] == disc[city], and then every city above it on group_stack is in its group. The first group completed has no flight leaving it.

Silent Failure Traps & Edge Cases

Where confident candidates still lose points

This problem's traps are in Common Pitfalls above.

The 60-Second Interview Pitch

Say this out loud before you type a single line

Every city must reach every other city, so I check whether the graph is one strongly connected group. I use Tarjan's algorithm, which is one depth-first search. I number the cities in the order I reach them, that number is disc, and I keep a stack of cities whose group is not complete. For each city, low is the smallest disc of a waiting city that its subtree can reach. When a city finishes with low equal to disc, it is the first city of a group, so I pop the stack down to it. The trap is a flight to a city that already left the stack: I use a neighbour only while it is still on the stack. One group means YES. Otherwise the first group completed has no flight out, so I name a city from it and a city outside it. The search is iterative, and the time and space are both linear in cities plus flights.

Big-O Invariant Derivation

Complexity & Mathematical Proof

TIME COMPLEXITY

O(V + E)

Look at the code: building adj touches each of the E flights once. The test disc[nxt] == 0 is True once per city, so each city gets one disc, one push on group_stack and one pop. Each frame of path has one iterator over its city's flights, so every flight is read once across the whole search, and each min takes constant time. The final loop over the cities is O(V). Total: O(V + E).

SPACE COMPLEXITY

O(V + E)

adj holds E entries: O(E). disc, low, on_stack and comp hold V + 1 entries each: O(V). group_stack and path hold at most one entry per city: O(V). Nothing is recursive, so there is no hidden call-stack cost.

Formal Recurrence Relation

T(V, E) = (V + E) [build adj] + (V + E) [one search with pushes, pops and low updates] + V [final loop] = O(V + E)

Derivation Progression

Build the flight lists

E appends: O(V + E)

Every flight is added once to adj.

The search

V pushes, V pops, E flight reads: O(V + E)

The disc[nxt] == 0 test lets each city enter the search once, and each city's iterator reads each flight once.

The answer

V steps: O(V)

The last loop reads comp once per city to find a city of the first group and a city outside it.

Variable Definitions

VVV

Number of cities, n

EEE

Number of flights, len(flights)

Memory Architecture & Bounds

🟣 Call Stack

O(1): the search uses an explicit path, no recursion

🔵 Auxiliary Heap

O(V + E): adj holds E entries; disc, low, on_stack, comp, group_stack and path hold O(V) each

🟢 Output Space

O(1): the answer has at most three items

Boundary Best / Worst Cases

Best Case

O(V+E)O(V + E)O(V+E): every city and flight is handled once on every input.

Average Case

O(V+E)O(V + E)O(V+E).

Worst Case

O(V+E)O(V + E)O(V+E).

Graph & Tree Traversal Frontier

Graph & Tree Traversal Frontier
Synthesizing vector architecture diagram...
Staff+ Engineering Perspective

Senior SWE Deconstruction & Hardware Caveats

ARCHITECTURAL SIGNALS & INTERVIEW TRIGGERS

Scale signal: checking every pair of cities with its own search costs about V⋅(V+E)=105⋅3×105=3×1010V \cdot (V + E) = 10^5 \cdot 3 \times 10^5 = 3 \times 10^{10}V⋅(V+E)=105⋅3×105=3×1010 steps; Tarjan's algorithm with one search costs about 3×1053 \times 10^53×105.

CONSTRAINTS & BOUNDS

n≤105n \le 10^5n≤105, m≤2×105m \le 2 \times 10^5m≤2×105. One chain of 10510^5105 flights makes the search path 10510^5105 cities deep, far past a recursion limit, so the search keeps its own path. Unlike Kosaraju's algorithm, it needs no reversed copy of the flights, so memory stays at one edge list.

FAANG PRODUCTION TRAPS & EDGE CASES

Use disc[nxt] only for a neighbour with on_stack[nxt] True, and clear on_stack only when the city is popped with its group. Group ids depend on the search order, so ids from two runs must never be compared. When flights are added or removed over time, groups can merge or split, so the simple production answer is to rerun the linear search.

Core Algorithmic State Invariants

  1. The Smallest Case: n = 2, flights = [[2, 1]]

City 1 has no flight out, so it finishes alone as group 0. City 2 has a flight to city 1, but city 1 already left group_stack, so on_stack[1] is False and low[2] stays 2. City 2 is a group of its own, comp_id is 2, and the answer is NO: no route leads from city 1 to city 2.

  1. One Cycle Is One Group

In n = 3, flights = [[1,2],[2,3],[3,1]], city 3 has a flight to city 1, and city 1 is on group_stack, so low[3] = 1. That value goes up to city 2 and then to city 1. Only city 1 finishes with low equal to disc, and it pops all three cities as group 0: comp_id is 1, so the answer is YES.

  1. The First Group Completed Has No Flight Out

A group is completed only when nothing below it climbs to an older waiting city, so every flight that leaves a city of the group leads to a group that is already complete. The first group has none to lead to. In Example 2, city 4 is group 0, so no route leads from city 4 to city 3.

Theory Context•Graph Algorithms
Medium

Flight Routes Check

A country has n cities, numbered 1 to n, and m one-way flights. A flight [a, b] takes you from city a to city b, and it does not take you back. A route is any sequence of flights, one after another.

Decide whether every city can reach every other city by some route. If it can, return ["YES"]. If it cannot, return ["NO", a, b], where a and b are two cities and no route leads from a to b. If several such pairs exist, any one of them is accepted.

Worked Examples

Example 1
Input:n = 3, flights = [[1,2],[2,3],[3,1]]
Output:["YES"]
1230
Explanation: The three cities form one cycle, so every city reaches every other city.
Example 2
Input:n = 4, flights = [[1,2],[2,3],[3,1],[1,4],[3,4]]
Output:["NO", 4, 3]
12340
Explanation: Cities 1, 2 and 3 can reach each other, but city 4 has no flight out. So no route leads from city 4 to any other city. Any pair that starts at city 4 is accepted, for example ["NO", 4, 1].

⚖️Formal Constraints & Bounds

  • 1 <= n <= 105

  • 1 <= m <= 2 * 105 (the number of flights)

  • 1 <= a, b <= n

Deep-Dive & Conceptual Insights

Core Insight

One depth-first search finds every group in which the cities reach each other. A city whose low equals its own disc is the first city of a group, and popping group_stack down to it collects the group. One group means every city reaches every other city. With more groups, the first group completed has no flight out, so it cannot reach any other group.

Real-World Scenario & Production Applications

Checking that a one-way network is connected in both directions: a set of one-way streets where every junction must be reachable from every other junction, or a set of services that must all be able to call each other through a chain of calls.

Step-by-Step Execution Trace Table

Step-by-Step Tarjan's Algorithm (n = 4, flights = [[1,2],[2,3],[3,1],[1,4],[3,4]])
  1. Step 1 (Start at 1): disc[1] = low[1] = 1, group_stack = [1].
  2. Step 2 (Down to 2, then 3): disc[2] = low[2] = 2 and disc[3] = low[3] = 3, so group_stack = [1, 2, 3].
  3. Step 3 (A Flight Back to a Waiting City): city 3 has a flight to city 1, and city 1 is on group_stack, so low[3] = min(3, disc[1]) = 1.
  4. Step 4 (Down to 4): city 3 also has a flight to city 4, which is new: disc[4] = low[4] = 4 and group_stack = [1, 2, 3, 4].
  5. Step 5 (City 4 Finishes): city 4 has no flight out and low[4] == disc[4], so group_stack is popped down to city 4. Group 0 is {4}, and group_stack = [1, 2, 3].
  6. Step 6 (Cities 3 and 2 Finish): low[3] = 1 is not disc[3] = 3, so city 3 stays on the stack and hands low = 1 to city 2. City 2 is also not a root, and it hands low = 1 to city 1.
  7. Step 7 (City 1 Sees City 4): city 4 was reached, but it is no longer on group_stack, so on_stack[4] is False and disc[4] is not used. City 1 finishes with low[1] == disc[1] == 1, so the stack is popped down to city 1. Group 1 is {3, 2, 1}.
  8. Step 8 (Answer): comp_id = 2, so the answer is NO. The first group completed is city 4, and no flight leaves it, so sink = 4 and other = 3. Return ["NO", 4, 3] ✅.
Full Walkthrough8 Steps
Input:n = 3, flights = [[1,2],[2,3],[3,1]]Expected:["YES"]
1⚡ STEP(Start at 1)
disc[1] = low[1] = 1, group_stack = [1].
2⚡ STEP(Down to 2, then 3)
disc[2] = low[2] = 2 and disc[3] = low[3] = 3, so group_stack = [1, 2, 3].
3⚡ STEP(A Flight Back to a Waiting City)
city 3 has a flight to city 1, and city 1 is on group_stack, so low[3] = min(3, disc[1]) = 1.
4⚡ STEP(Down to 4)
city 3 also has a flight to city 4, which is new: disc[4] = low[4] = 4 and group_stack = [1, 2, 3, 4].
5↩️ BACKTRACK / POP(City 4 Finishes)
city 4 has no flight out and low[4] == disc[4], so group_stack is popped down to city 4. Group 0 is {4}, and group_stack = [1, 2, 3].
6⚡ STEP(Cities 3 and 2 Finish)
low[3] = 1 is not disc[3] = 3, so city 3 stays on the stack and hands low = 1 to city 2. City 2 is also not a root, and it hands low = 1 to city 1.
7↩️ BACKTRACK / POP(City 1 Sees City 4)
city 4 was reached, but it is no longer on group_stack, so on_stack[4] is False and disc[4] is not used. City 1 finishes with low[1] == disc[1] == 1, so the stack is popped down to city 1. Group 1 is {3, 2, 1}.
8✅ RECORD / GOAL(Answer)
comp_id = 2, so the answer is NO. The first group completed is city 4, and no flight leaves it, so sink = 4 and other = 3. Return ["NO", 4, 3] ✅.
Code Shape
Archetype Code Shape
Python
1adj = the flights that leave each city
2disc, low, on_stack, group_stack = the search state
3for start in 1..n:
4 if start is not reached yet: search from start
5 a new city gets the next disc and waits on group_stack
6 a reached city that still waits lowers low[city] to its disc
7 a finished city hands its low to its parent
8 if low[city] == disc[city]: pop group_stack down to city as one group
9return YES for one group, else NO with a city of the first group and a city outside it

Conceptual Narrative

🧭 Conceptual Foundation & Pattern Intuition

Tarjan's algorithm finds every strongly connected group of a directed graph with one depth-first search. A group is a set of cities where each city can reach every other city of the set. In Flight Routes Check, one group that holds all the cities means that every city can reach every other city.

The search gives each city a number disc when it first reaches the city, and it puts the city on a stack called group_stack. A city on this stack is waiting: it has been reached, but its group is not complete. For each city, low is the smallest disc of a waiting city that the city's subtree can reach. When a city has tried all of its flights and low[city] == disc[city], nothing below it can climb above it. That city is the first city of a group, and the search pops group_stack down to it to collect the group.

🏨 The Analogy: A Waiting Room With Numbered Tickets

Each city takes a ticket number when the search first arrives, and it sits in a waiting room. A flight from the current city to a city that still sits in the room means that the current city can fly back to an older ticket, so its group cannot leave yet. A flight to a city that already left the room leads to a group that is complete, and it is ignored. When the current city's own ticket is the smallest number it can reach, no city behind it in the room can reach an older ticket. That city and every city that arrived after it and still waits leave the room together as one group.

🔑 The Key Idea in the Code

Two lines decide the result. The line if on_stack[nxt]: lets a reached city lower low[city] only while that city still waits, because a city in a complete group cannot lead back. The line if low[city] == disc[city]: marks the first city of a group, and the loop that pops group_stack until it reaches city collects the whole group.

Kosaraju's algorithm gets the same groups with two passes and a reversed copy of the graph. Tarjan's algorithm needs one pass over the original graph, and it keeps the extra arrays low and on_stack instead.

  • Using disc[nxt] for every reached city (the trap): low[city] = min(low[city], disc[nxt]) must sit under if on_stack[nxt]:. A city that already left group_stack is in a complete group, and using it keeps city from being popped.

  • Clearing on_stack when the search leaves a city: the flag stays True until the city is popped with its group. If it is cleared earlier, a later flight to a waiting city is ignored.

  • Not handing low up to the parent: low[parent] = min(low[parent], low[city]) comes after path.pop(). Without it, a flight back to an older city never reaches the cities in between.

  • Recursive DFS at scale: with n up to 105 cities, a recursive search passes Python's recursion limit on one long chain of flights. The search keeps its own path.

Senior SWE Reasoning Architecture

4-Phase Thought Process Model

Pattern Recognition Signals

The 10-second spot

"Whether every city can reach every other city" over "one-way flights": reaching each other in both directions is the definition of a strongly connected group. If the whole graph is one group, the answer is YES. If not, the answer needs a pair with no route. With up to 105 cities and 2 * 105 flights, a search from every city is too slow, so the answer has to come from one linear search.

Formulating the Predicate & Invariants

Turning intuition into a boolean rule

group_stack holds the reached cities whose group is not complete. low[city] is the smallest disc of a waiting city that city's subtree reaches. A city is the first city of its group exactly when low[city] == disc[city], and then every city above it on group_stack is in its group. The first group completed has no flight leaving it.

Silent Failure Traps & Edge Cases

Where confident candidates still lose points

This problem's traps are in Common Pitfalls above.

The 60-Second Interview Pitch

Say this out loud before you type a single line

Every city must reach every other city, so I check whether the graph is one strongly connected group. I use Tarjan's algorithm, which is one depth-first search. I number the cities in the order I reach them, that number is disc, and I keep a stack of cities whose group is not complete. For each city, low is the smallest disc of a waiting city that its subtree can reach. When a city finishes with low equal to disc, it is the first city of a group, so I pop the stack down to it. The trap is a flight to a city that already left the stack: I use a neighbour only while it is still on the stack. One group means YES. Otherwise the first group completed has no flight out, so I name a city from it and a city outside it. The search is iterative, and the time and space are both linear in cities plus flights.

Big-O Invariant Derivation

Complexity & Mathematical Proof

TIME COMPLEXITY

O(V + E)

Look at the code: building adj touches each of the E flights once. The test disc[nxt] == 0 is True once per city, so each city gets one disc, one push on group_stack and one pop. Each frame of path has one iterator over its city's flights, so every flight is read once across the whole search, and each min takes constant time. The final loop over the cities is O(V). Total: O(V + E).

SPACE COMPLEXITY

O(V + E)

adj holds E entries: O(E). disc, low, on_stack and comp hold V + 1 entries each: O(V). group_stack and path hold at most one entry per city: O(V). Nothing is recursive, so there is no hidden call-stack cost.

Formal Recurrence Relation

T(V, E) = (V + E) [build adj] + (V + E) [one search with pushes, pops and low updates] + V [final loop] = O(V + E)

Derivation Progression

Build the flight lists

E appends: O(V + E)

Every flight is added once to adj.

The search

V pushes, V pops, E flight reads: O(V + E)

The disc[nxt] == 0 test lets each city enter the search once, and each city's iterator reads each flight once.

The answer

V steps: O(V)

The last loop reads comp once per city to find a city of the first group and a city outside it.

Variable Definitions

VVV

Number of cities, n

EEE

Number of flights, len(flights)

Memory Architecture & Bounds

🟣 Call Stack

O(1): the search uses an explicit path, no recursion

🔵 Auxiliary Heap

O(V + E): adj holds E entries; disc, low, on_stack, comp, group_stack and path hold O(V) each

🟢 Output Space

O(1): the answer has at most three items

Boundary Best / Worst Cases

Best Case

O(V+E)O(V + E)O(V+E): every city and flight is handled once on every input.

Average Case

O(V+E)O(V + E)O(V+E).

Worst Case

O(V+E)O(V + E)O(V+E).

Graph & Tree Traversal Frontier

Graph & Tree Traversal Frontier
Synthesizing vector architecture diagram...
Staff+ Engineering Perspective

Senior SWE Deconstruction & Hardware Caveats

ARCHITECTURAL SIGNALS & INTERVIEW TRIGGERS

Scale signal: checking every pair of cities with its own search costs about V⋅(V+E)=105⋅3×105=3×1010V \cdot (V + E) = 10^5 \cdot 3 \times 10^5 = 3 \times 10^{10}V⋅(V+E)=105⋅3×105=3×1010 steps; Tarjan's algorithm with one search costs about 3×1053 \times 10^53×105.

CONSTRAINTS & BOUNDS

n≤105n \le 10^5n≤105, m≤2×105m \le 2 \times 10^5m≤2×105. One chain of 10510^5105 flights makes the search path 10510^5105 cities deep, far past a recursion limit, so the search keeps its own path. Unlike Kosaraju's algorithm, it needs no reversed copy of the flights, so memory stays at one edge list.

FAANG PRODUCTION TRAPS & EDGE CASES

Use disc[nxt] only for a neighbour with on_stack[nxt] True, and clear on_stack only when the city is popped with its group. Group ids depend on the search order, so ids from two runs must never be compared. When flights are added or removed over time, groups can merge or split, so the simple production answer is to rerun the linear search.

Core Algorithmic State Invariants

  1. The Smallest Case: n = 2, flights = [[2, 1]]

City 1 has no flight out, so it finishes alone as group 0. City 2 has a flight to city 1, but city 1 already left group_stack, so on_stack[1] is False and low[2] stays 2. City 2 is a group of its own, comp_id is 2, and the answer is NO: no route leads from city 1 to city 2.

  1. One Cycle Is One Group

In n = 3, flights = [[1,2],[2,3],[3,1]], city 3 has a flight to city 1, and city 1 is on group_stack, so low[3] = 1. That value goes up to city 2 and then to city 1. Only city 1 finishes with low equal to disc, and it pops all three cities as group 0: comp_id is 1, so the answer is YES.

  1. The First Group Completed Has No Flight Out

A group is completed only when nothing below it climbs to an older waiting city, so every flight that leaves a city of the group leads to a group that is already complete. The first group has none to lead to. In Example 2, city 4 is group 0, so no route leads from city 4 to city 3.

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