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•Breadth-First Search (BFS)
EasyLC 1971

Find if Path Exists in Graph (LeetCode 1971)

Target Frequency:AmazonMetaGoogle

A graph has n nodes, numbered from 0 to n - 1. The list edges describes its connections. Each pair [u, v] is one edge, and it joins node u and node v in both directions, so you can walk it either way.

You start at node source. Return true if you can walk along edges to node destination, and false if you cannot.

Worked Examples

Example 1
Input:n = 3, edges = [[0,1],[1,2],[2,0]], source = 0, destination = 2
Output:true
012
Explanation: Two paths lead from node 0 to node 2. One is 0 -> 1 -> 2. The other is 0 -> 2, which uses the edge [2, 0] in the direction from 0 to 2.
Example 2
Input:n = 6, edges = [[0,1],[0,2],[3,5],[5,4],[4,3]], source = 0, destination = 5
Output:false
012354
Explanation: Nodes 0, 1 and 2 are joined to each other. Nodes 3, 4 and 5 are joined to each other. No edge joins the two groups, so node 0 cannot reach node 5.

⚖️Formal Constraints & Bounds

  • 1 <= n <= 2 * 105

  • 0 <= edges.length <= 2 * 105

  • edges[i].length == 2

  • 0 <= ui, vi <= n - 1

  • ui != vi

  • 0 <= source, destination <= n - 1

  • There are no duplicate edges.

  • There are no self-edges.

Deep-Dive & Conceptual Insights

Core Insight

Mark a node in seen at the moment it is added to queue: a node with two parents is then queued once, and a cycle cannot refill the queue, so the loop always ends.

Real-World Scenario & Production Applications

Checking whether one computer can send data to another computer in a network, or whether one station can reach another station on a railway map, is the same question.

Step-by-Step Execution Trace Table

Step-by-Step Decision Trace (Example 2)
  1. Step 1 (source is not destination): source = 0 and destination = 5 are different nodes, so a search is needed.
  2. Step 2 (build graph): every edge is stored in both directions. graph = {0: [1, 2], 1: [0], 2: [0], 3: [5, 4], 5: [3, 4], 4: [5, 3]}.
  3. Step 3 (start): seen = {0} and queue = [0]. The source is marked before the loop starts.
  4. Step 4 (pop 0): node = 0. Neighbour 1 is not the destination and is not in seen, so seen = {0, 1} and queue = [1]. Neighbour 2 is new too, so seen = {0, 1, 2} and queue = [1, 2].
  5. Step 5 (pop 1): node = 1. Its only neighbour is 0. Node 0 is already in seen, so it is not queued again.
  6. Step 6 (pop 2): node = 2. Its only neighbour is 0, which is already in seen. Nothing is queued.
  7. Step 7 (empty queue): queue = []. Every node that can be reached from node 0 was expanded, and none of them is node 5.

Search complete: return False.

Full Walkthrough7 Steps
Input:n = 3, edges = [[0,1],[1,2],[2,0]], source = 0, destination = 2Expected:true
1⚡ STEP(source is not destination)
source = 0 and destination = 5 are different nodes, so a search is needed.
2⚡ STEP(build `graph`)
every edge is stored in both directions. graph = {0: [1, 2], 1: [0], 2: [0], 3: [5, 4], 5: [3, 4], 4: [5, 3]}.
3⚡ STEP(start)
queue = [0]
seen = {0} and queue = [0]. The source is marked before the loop starts.
4⚡ FIFO DRAIN(pop 0)
queue = [1]
node = 0. Neighbour 1 is not the destination and is not in seen, so seen = {0, 1} and queue = [1]. Neighbour 2 is new too, so seen = {0, 1, 2} and queue = [1, 2].
5⚡ FIFO DRAIN(pop 1)
node = 1. Its only neighbour is 0. Node 0 is already in seen, so it is not queued again.
6⚡ FIFO DRAIN(pop 2)
node = 2. Its only neighbour is 0, which is already in seen. Nothing is queued.
7⚡ STEP(empty queue)
queue = []
queue = []. Every node that can be reached from node 0 was expanded, and none of them is node 5. Search complete: return False.
Code Shape
Archetype Code Shape
Python
1if source == destination: return True
2graph = lists of neighbours, built from edges (both directions)
3seen = {source}
4queue = deque([source])
5while queue:
6 node = queue.popleft()
7 for neighbour in graph[node]:
8 if neighbour == destination: return True
9 if neighbour not in seen:
10 seen.add(neighbour)
11 queue.append(neighbour)
12return False

Conceptual Narrative

🧭 Conceptual Foundation & Pattern Intuition

A graph is a set of nodes joined by edges. In a tree, every node has one parent. In a graph, one node can be reached from two parents, and the edges can form a cycle. A cycle is a path that comes back to the node where it started.

Breadth-first search (BFS) on a graph uses the same queue as level-order traversal. It adds one rule: a set named seen records every node that has been added to queue. A node that is already in seen is never added again.

🚆 Real-World Example: Train Stations

Each station is a node and each track between two stations is an edge. You start at one station and want to know whether you can reach another station. You keep a list of the stations that you have already planned to visit. Before you plan a station, you check the list. Without the list, two tracks that lead to the same station, or a loop of tracks, would put the same station in your plan again and again.

🔑 Why It Works

seen.add(neighbour) runs in the same step as queue.append(neighbour). A node that waits in queue is therefore already marked, so a second parent cannot add it again. Each node enters queue once, so the loop runs at most n times and ends even when the graph has a cycle.

  • Adding a node without checking seen: A graph can have a cycle. Without the check, every neighbour goes into queue again and again, so the loop never ends when destination cannot be reached. Add a node to seen at the moment it joins queue.

  • Adding only one direction of each edge: An edge [u, v] joins both nodes. Add v to the neighbours of u and u to the neighbours of v, or a search that starts at v finds no neighbour.

  • Skipping the case source == destination: A path of length 0 exists, even when edges is empty. Return True before the search starts.

  • Marking a node when it leaves queue instead of when it joins: A node that waits in queue is not yet in seen, so a second parent adds it again and the same node is expanded many times.

Senior SWE Reasoning Architecture

4-Phase Thought Process Model

Pattern Recognition Signals

The 10-second spot

The statement gives n nodes and a list of two-way edges, and asks whether destination can be "reached" from source. The input is a graph that can have cycles, and the search starts from one node. That is Graph BFS with a seen set.

Formulating the Predicate & Invariants

Turning intuition into a boolean rule

Invariant: every node in seen is in queue or has already left it, so each node is added to queue exactly once. The decision: a neighbour that is not in seen joins seen and queue together, and a neighbour that is already in seen is skipped.

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

"I'd use BFS with a seen set. If source is destination, I return True at once. Otherwise I build a list of neighbours for each node from the edges, adding each edge in both directions, because it can be walked either way. I mark source in seen, put it in the queue, and repeat: take the oldest node, and look at each of its neighbours. If a neighbour is the destination, I return True. If it is not in seen, I add it to seen and to the queue in the same step. So every node waits in the queue at most once, a node with two parents is expanded once, and a cycle cannot keep the loop running. If the queue becomes empty, every node that can be reached was expanded, so I return False. Each node and each edge is handled a constant number of times, so the time is O(V + E), and the space is O(V + E) for the lists, seen and the queue."

Big-O Invariant Derivation

Complexity & Mathematical Proof

TIME COMPLEXITY

O(V + E)

Look at the code: for u, v in edges: reads each of the E edges once and does two appends. A node enters queue only when it is not in seen, and it joins seen in the same step, so each of the V nodes is added at most once and popped at most once. When a node is popped, for neighbour in graph[node]: reads its list, and the lists together hold 2E entries. Total O(V + E).

SPACE COMPLEXITY

O(V + E)

graph holds 2E entries for V nodes, which is O(V + E). seen and queue each hold at most V nodes, which is O(V). Total O(V + E). The loop is not recursive, so the call stack is O(1).

Formal Recurrence Relation

T(V, E) = E (build lists) + V (pushes) + V (pops) + 2E (neighbour reads) = O(V + E)

Derivation Progression

Build the Lists

for u, v in edges: graph[u].append(v); graph[v].append(u) → E steps

Each edge is read once and stored in both directions, which is 2 appends.

Pushes and Pops

seen.add(neighbour); queue.append(neighbour) → at most V pushes and V pops

A node joins queue only if it is not in seen, and it joins seen in the same step, so no node is pushed twice.

Neighbour Reads

for neighbour in graph[node] → 2E reads over all pops

The neighbour lists hold 2E entries together, and each list is read once, when its node is popped.

Variable Definitions

VVV

Number of nodes in the graph (n)

EEE

Number of edges, len(edges)

Memory Architecture & Bounds

🟣 Call Stack

O(1): no recursion

🔵 Auxiliary Heap

O(V + E): graph holds 2E entries; seen and queue hold at most V nodes each

🟢 Output Space

O(1): one boolean

Boundary Best / Worst Cases

Best Case

O(1)O(1)O(1) when source is destination, and O(E)O(E)O(E) otherwise because the lists are always built first

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

Triggers: "edges" that join two nodes, and the question whether "a valid path from source to destination" exists: reachability in an undirected graph, so BFS with a seen set. At scale the same traversal answers whether two machines are in the same network segment.

CONSTRAINTS & BOUNDS

n≤2⋅105n \le 2 \cdot 10^5n≤2⋅105 nodes and E≤2⋅105E \le 2 \cdot 10^5E≤2⋅105 edges. One pass over the nodes and the edges is O(V+E)O(V + E)O(V+E), about 6⋅1056 \cdot 10^56⋅105 steps. A recursive DFS can go 2⋅1052 \cdot 10^52⋅105 calls deep, which is more than Python's default call stack allows, so the loop with queue is also the safe choice here.

FAANG PRODUCTION TRAPS & EDGE CASES

Skipping the check if neighbour not in seen: lets the nodes of a cycle refill queue, and the loop never ends when destination cannot be reached. Adding only one direction of each edge hides every path that starts at the other end. Marking a node when it leaves queue is correct but slow: on a complete graph of 10 nodes, queue receives 45 pushes instead of 9.

Theory Context•Breadth-First Search (BFS)
EasyLC 1971

Find if Path Exists in Graph (LeetCode 1971)

Target Frequency:AmazonMetaGoogle

A graph has n nodes, numbered from 0 to n - 1. The list edges describes its connections. Each pair [u, v] is one edge, and it joins node u and node v in both directions, so you can walk it either way.

You start at node source. Return true if you can walk along edges to node destination, and false if you cannot.

Worked Examples

Example 1
Input:n = 3, edges = [[0,1],[1,2],[2,0]], source = 0, destination = 2
Output:true
012
Explanation: Two paths lead from node 0 to node 2. One is 0 -> 1 -> 2. The other is 0 -> 2, which uses the edge [2, 0] in the direction from 0 to 2.
Example 2
Input:n = 6, edges = [[0,1],[0,2],[3,5],[5,4],[4,3]], source = 0, destination = 5
Output:false
012354
Explanation: Nodes 0, 1 and 2 are joined to each other. Nodes 3, 4 and 5 are joined to each other. No edge joins the two groups, so node 0 cannot reach node 5.

⚖️Formal Constraints & Bounds

  • 1 <= n <= 2 * 105

  • 0 <= edges.length <= 2 * 105

  • edges[i].length == 2

  • 0 <= ui, vi <= n - 1

  • ui != vi

  • 0 <= source, destination <= n - 1

  • There are no duplicate edges.

  • There are no self-edges.

Deep-Dive & Conceptual Insights

Core Insight

Mark a node in seen at the moment it is added to queue: a node with two parents is then queued once, and a cycle cannot refill the queue, so the loop always ends.

Real-World Scenario & Production Applications

Checking whether one computer can send data to another computer in a network, or whether one station can reach another station on a railway map, is the same question.

Step-by-Step Execution Trace Table

Step-by-Step Decision Trace (Example 2)
  1. Step 1 (source is not destination): source = 0 and destination = 5 are different nodes, so a search is needed.
  2. Step 2 (build graph): every edge is stored in both directions. graph = {0: [1, 2], 1: [0], 2: [0], 3: [5, 4], 5: [3, 4], 4: [5, 3]}.
  3. Step 3 (start): seen = {0} and queue = [0]. The source is marked before the loop starts.
  4. Step 4 (pop 0): node = 0. Neighbour 1 is not the destination and is not in seen, so seen = {0, 1} and queue = [1]. Neighbour 2 is new too, so seen = {0, 1, 2} and queue = [1, 2].
  5. Step 5 (pop 1): node = 1. Its only neighbour is 0. Node 0 is already in seen, so it is not queued again.
  6. Step 6 (pop 2): node = 2. Its only neighbour is 0, which is already in seen. Nothing is queued.
  7. Step 7 (empty queue): queue = []. Every node that can be reached from node 0 was expanded, and none of them is node 5.

Search complete: return False.

Full Walkthrough7 Steps
Input:n = 3, edges = [[0,1],[1,2],[2,0]], source = 0, destination = 2Expected:true
1⚡ STEP(source is not destination)
source = 0 and destination = 5 are different nodes, so a search is needed.
2⚡ STEP(build `graph`)
every edge is stored in both directions. graph = {0: [1, 2], 1: [0], 2: [0], 3: [5, 4], 5: [3, 4], 4: [5, 3]}.
3⚡ STEP(start)
queue = [0]
seen = {0} and queue = [0]. The source is marked before the loop starts.
4⚡ FIFO DRAIN(pop 0)
queue = [1]
node = 0. Neighbour 1 is not the destination and is not in seen, so seen = {0, 1} and queue = [1]. Neighbour 2 is new too, so seen = {0, 1, 2} and queue = [1, 2].
5⚡ FIFO DRAIN(pop 1)
node = 1. Its only neighbour is 0. Node 0 is already in seen, so it is not queued again.
6⚡ FIFO DRAIN(pop 2)
node = 2. Its only neighbour is 0, which is already in seen. Nothing is queued.
7⚡ STEP(empty queue)
queue = []
queue = []. Every node that can be reached from node 0 was expanded, and none of them is node 5. Search complete: return False.
Code Shape
Archetype Code Shape
Python
1if source == destination: return True
2graph = lists of neighbours, built from edges (both directions)
3seen = {source}
4queue = deque([source])
5while queue:
6 node = queue.popleft()
7 for neighbour in graph[node]:
8 if neighbour == destination: return True
9 if neighbour not in seen:
10 seen.add(neighbour)
11 queue.append(neighbour)
12return False

Conceptual Narrative

🧭 Conceptual Foundation & Pattern Intuition

A graph is a set of nodes joined by edges. In a tree, every node has one parent. In a graph, one node can be reached from two parents, and the edges can form a cycle. A cycle is a path that comes back to the node where it started.

Breadth-first search (BFS) on a graph uses the same queue as level-order traversal. It adds one rule: a set named seen records every node that has been added to queue. A node that is already in seen is never added again.

🚆 Real-World Example: Train Stations

Each station is a node and each track between two stations is an edge. You start at one station and want to know whether you can reach another station. You keep a list of the stations that you have already planned to visit. Before you plan a station, you check the list. Without the list, two tracks that lead to the same station, or a loop of tracks, would put the same station in your plan again and again.

🔑 Why It Works

seen.add(neighbour) runs in the same step as queue.append(neighbour). A node that waits in queue is therefore already marked, so a second parent cannot add it again. Each node enters queue once, so the loop runs at most n times and ends even when the graph has a cycle.

  • Adding a node without checking seen: A graph can have a cycle. Without the check, every neighbour goes into queue again and again, so the loop never ends when destination cannot be reached. Add a node to seen at the moment it joins queue.

  • Adding only one direction of each edge: An edge [u, v] joins both nodes. Add v to the neighbours of u and u to the neighbours of v, or a search that starts at v finds no neighbour.

  • Skipping the case source == destination: A path of length 0 exists, even when edges is empty. Return True before the search starts.

  • Marking a node when it leaves queue instead of when it joins: A node that waits in queue is not yet in seen, so a second parent adds it again and the same node is expanded many times.

Senior SWE Reasoning Architecture

4-Phase Thought Process Model

Pattern Recognition Signals

The 10-second spot

The statement gives n nodes and a list of two-way edges, and asks whether destination can be "reached" from source. The input is a graph that can have cycles, and the search starts from one node. That is Graph BFS with a seen set.

Formulating the Predicate & Invariants

Turning intuition into a boolean rule

Invariant: every node in seen is in queue or has already left it, so each node is added to queue exactly once. The decision: a neighbour that is not in seen joins seen and queue together, and a neighbour that is already in seen is skipped.

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

"I'd use BFS with a seen set. If source is destination, I return True at once. Otherwise I build a list of neighbours for each node from the edges, adding each edge in both directions, because it can be walked either way. I mark source in seen, put it in the queue, and repeat: take the oldest node, and look at each of its neighbours. If a neighbour is the destination, I return True. If it is not in seen, I add it to seen and to the queue in the same step. So every node waits in the queue at most once, a node with two parents is expanded once, and a cycle cannot keep the loop running. If the queue becomes empty, every node that can be reached was expanded, so I return False. Each node and each edge is handled a constant number of times, so the time is O(V + E), and the space is O(V + E) for the lists, seen and the queue."

Big-O Invariant Derivation

Complexity & Mathematical Proof

TIME COMPLEXITY

O(V + E)

Look at the code: for u, v in edges: reads each of the E edges once and does two appends. A node enters queue only when it is not in seen, and it joins seen in the same step, so each of the V nodes is added at most once and popped at most once. When a node is popped, for neighbour in graph[node]: reads its list, and the lists together hold 2E entries. Total O(V + E).

SPACE COMPLEXITY

O(V + E)

graph holds 2E entries for V nodes, which is O(V + E). seen and queue each hold at most V nodes, which is O(V). Total O(V + E). The loop is not recursive, so the call stack is O(1).

Formal Recurrence Relation

T(V, E) = E (build lists) + V (pushes) + V (pops) + 2E (neighbour reads) = O(V + E)

Derivation Progression

Build the Lists

for u, v in edges: graph[u].append(v); graph[v].append(u) → E steps

Each edge is read once and stored in both directions, which is 2 appends.

Pushes and Pops

seen.add(neighbour); queue.append(neighbour) → at most V pushes and V pops

A node joins queue only if it is not in seen, and it joins seen in the same step, so no node is pushed twice.

Neighbour Reads

for neighbour in graph[node] → 2E reads over all pops

The neighbour lists hold 2E entries together, and each list is read once, when its node is popped.

Variable Definitions

VVV

Number of nodes in the graph (n)

EEE

Number of edges, len(edges)

Memory Architecture & Bounds

🟣 Call Stack

O(1): no recursion

🔵 Auxiliary Heap

O(V + E): graph holds 2E entries; seen and queue hold at most V nodes each

🟢 Output Space

O(1): one boolean

Boundary Best / Worst Cases

Best Case

O(1)O(1)O(1) when source is destination, and O(E)O(E)O(E) otherwise because the lists are always built first

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

Triggers: "edges" that join two nodes, and the question whether "a valid path from source to destination" exists: reachability in an undirected graph, so BFS with a seen set. At scale the same traversal answers whether two machines are in the same network segment.

CONSTRAINTS & BOUNDS

n≤2⋅105n \le 2 \cdot 10^5n≤2⋅105 nodes and E≤2⋅105E \le 2 \cdot 10^5E≤2⋅105 edges. One pass over the nodes and the edges is O(V+E)O(V + E)O(V+E), about 6⋅1056 \cdot 10^56⋅105 steps. A recursive DFS can go 2⋅1052 \cdot 10^52⋅105 calls deep, which is more than Python's default call stack allows, so the loop with queue is also the safe choice here.

FAANG PRODUCTION TRAPS & EDGE CASES

Skipping the check if neighbour not in seen: lets the nodes of a cycle refill queue, and the loop never ends when destination cannot be reached. Adding only one direction of each edge hides every path that starts at the other end. Marking a node when it leaves queue is correct but slow: on a complete graph of 10 nodes, queue receives 45 pushes instead of 9.

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