Graphs are one of the most important abstractions in algorithmic problem solving.
They allow us to represent relationships:
People connected to people
Services connected to services
Cities connected by roads
Machines connected through networks
Pages connected by links
States connected by transitions
The interview-preparation material places Graphs in the advanced algorithmic sequence alongside recursion, Dynamic Programming, and Greedy Algorithms. It also identifies graph modeling as a core algorithmic skill.
For a Staff/Principal engineer, the important question is not simply:
“Do you know BFS and DFS?”
It is:
“Can you recognize when a problem is really a graph, model it correctly, choose the right traversal, and explain the trade-offs?”
1. What Is a Graph?
A graph consists of:
Vertices / Nodes
+
Edges / Relationships
For example:
A ─── B
│ │
│ │
C ─── D
Here:
Vertices = {A, B, C, D}
Edges =
(A,B)
(A,C)
(B,D)
(C,D)
The important abstraction is:
Entity
↓
Relationship
↓
Graph
2. Why Graph Modeling Matters
Many interview problems do not explicitly say:
“You are given a graph.”
Instead, they describe relationships.
For example:
A depends on B
B depends on C
can become:
A → B → C
Similarly:
City A connected to City B
City B connected to City C
becomes:
A ─ B ─ C
The Staff-level skill is recognizing the hidden graph.
3. The Graph Recognition Pattern
When reading a problem, ask:
Are there entities?
↓
Are there relationships between them?
↓
Can I represent those relationships as edges?
↓
Is the question about reachability,
connectivity, paths, dependencies, or cycles?
↓
Think Graph
The source’s pattern-recognition map explicitly associates connections with Graphs and hierarchies with trees/DFS/BFS.
4. Directed vs Undirected Graphs
Undirected
If:
A connected to B
then:
```text
A ─ B
The relationship works in both directions.
Directed
If:
A depends on B
then:
A → B
The direction matters.
Always clarify this before implementing.
5. Weighted vs Unweighted Graphs
An edge may simply represent a relationship:
A ─ B
or carry a cost:
A ──5── B
The weight could represent:
distance
latency
cost
time
risk
capacity
This distinction often determines the shortest-path algorithm.
6. Graph Representation
The most common representation for interview problems is an adjacency list.
For:
A ─ B
│
C
we can store:
graph = {
"A": ["B", "C"],
"B": ["A"],
"C": ["A"],
}
For a directed graph:
graph = {
"A": ["B"],
"B": ["C"],
"C": [],
}
The representation should match the operations the algorithm needs.
7. Adjacency Matrix
Another representation is an adjacency matrix:
A B C
A 0 1 1
B 1 0 0
C 1 0 0
Conceptually:
matrix[u][v] = 1
if an edge exists.
Matrix:
Space = O(V²)
Adjacency list:
Space = O(V + E)
For sparse graphs, adjacency lists are usually much more memory efficient.
8. BFS — Breadth-First Search
BFS explores a graph level by level.
Start
↓
All immediate neighbors
↓
Neighbors of neighbors
↓
Next level
↓
...
The natural data structure is a queue.
The source explicitly associates queues with BFS and level-order traversal.
In Python:
from collections import deque
def bfs(graph, start):
visited = {start}
queue = deque([start])
while queue:
node = queue.popleft()
for neighbor in graph[node]:
if neighbor not in visited:
visited.add(neighbor)
queue.append(neighbor)
9. Why BFS Uses a Queue
A queue gives:
First discovered
↓
First processed
Therefore nodes are processed in layers:
distance 0
↓
distance 1
↓
distance 2
↓
distance 3
This makes BFS particularly useful for shortest paths in unweighted graphs.
10. BFS Shortest Path
Suppose:
A ─ B ─ D
│
C ───── D
Starting from A, BFS explores:
A
↓
B, C
↓
D
The first time BFS reaches a node in an unweighted graph, it has found a shortest path in terms of number of edges.
A typical implementation stores distance:
from collections import deque
def shortest_distance(graph, start, target):
queue = deque([(start, 0)])
visited = {start}
while queue:
node, distance = queue.popleft()
if node == target:
return distance
for neighbor in graph[node]:
if neighbor not in visited:
visited.add(neighbor)
queue.append((neighbor, distance + 1))
return -1
11. DFS — Depth-First Search
DFS explores one path deeply before backtracking.
Conceptually:
Start
↓
Neighbor
↓
Neighbor
↓
Neighbor
↓
Backtrack
DFS can be implemented recursively:
def dfs(graph, node, visited):
if node in visited:
return
visited.add(node)
for neighbor in graph[node]:
dfs(graph, neighbor, visited)
Or iteratively:
def dfs(graph, start):
visited = {start}
stack = [start]
while stack:
node = stack.pop()
for neighbor in graph[node]:
if neighbor not in visited:
visited.add(neighbor)
stack.append(neighbor)
The source identifies stacks with DFS.
12. BFS vs DFS
| Requirement | BFS | DFS |
|---|---|---|
| Data structure | Queue | Stack / recursion |
| Level-order exploration | Excellent | No |
| Unweighted shortest path | Excellent | Not generally |
| Connectivity | Yes | Yes |
| Cycle detection | Yes | Yes |
| Topological reasoning | Sometimes | Often useful |
| Memory shape | Frontier | Current path |
A useful rule:
Shortest number of edges?
→ BFS
Explore structure deeply?
→ DFS
But don’t rely only on memorized rules.
Understand the graph’s structure and the required output.
13. The Most Important Graph Invariant
For traversal, maintain:
visited
The invariant is:
Every node in
visitedhas already been discovered and will not be processed again.
Without this, a cyclic graph can cause repeated traversal:
A → B
↑ ↓
└── C
A traversal without visited-state management can continue indefinitely.
14. Graph Complexity
For an adjacency-list representation:
V = number of vertices
E = number of edges
A complete traversal is typically:
Time = O(V + E)
Space = O(V)
Why?
Every vertex is discovered at most once, and every adjacency relationship is examined a bounded number of times.
This is the graph analogue of the source’s broader emphasis on explicitly explaining complexity rather than merely saying an algorithm is “fast.”
15. Connected Components
For an undirected graph:
A ─ B C ─ D
there are two connected components.
Algorithm:
for every unvisited node:
run BFS/DFS
count one component
Python:
def count_components(graph):
visited = set()
count = 0
for node in graph:
if node in visited:
continue
count += 1
stack = [node]
visited.add(node)
while stack:
current = stack.pop()
for neighbor in graph[current]:
if neighbor not in visited:
visited.add(neighbor)
stack.append(neighbor)
return count
Complexity:
O(V + E)
16. Cycle Detection
Cycles are a fundamental graph property.
Undirected:
A ─ B
│ │
C ──┘
Directed:
A → B → C
↑ ↓
└───────┘
The exact cycle-detection technique depends on whether the graph is directed or undirected.
That distinction is important in interviews.
17. Directed Graph Cycle Detection
For directed graphs, DFS can track the current recursion path.
Conceptually:
visited
+
currently_in_path
If DFS reaches a node already in the current path:
cycle exists
A typical state model is:
0 = unvisited
1 = visiting
2 = completed
If an edge points to a visiting node:
back edge
→ cycle
18. Topological Ordering
Directed acyclic graphs often represent dependencies:
Database
↓
Backend
↓
API
↓
Frontend
A topological ordering gives:
Database
Backend
API
Frontend
such that every dependency appears before the thing that depends on it.
This pattern appears in:
build systems
course prerequisites
package dependencies
workflow execution
deployment pipelines
task scheduling
A crucial condition is:
Topological ordering exists
⇔
Directed graph has no cycle
19. Kahn’s Algorithm
One common approach uses indegrees.
For every node:
indegree[node] =
number of incoming edges
Then:
Nodes with indegree 0
↓
Process
↓
Remove outgoing edges
↓
New indegree-0 nodes
↓
Repeat
This naturally uses a queue.
The pattern is:
dependency graph
↓
indegree
↓
queue
↓
topological order
20. DFS Topological Ordering
Another approach is DFS.
Conceptually:
DFS children
↓
finish node
↓
append node
Finally:
reverse finishing order
gives a topological ordering when the graph is acyclic.
The choice between Kahn’s algorithm and DFS is often a good Staff-level discussion point.
21. Shortest Paths
Graph shortest-path problems depend heavily on edge weights.
Unweighted
BFS
Non-negative weighted edges
Dijkstra
Negative edges
Bellman-Ford-style reasoning
DAG
Topological-order shortest path
The important Staff-level skill is not memorizing algorithms independently.
Instead:
Graph properties
+
Edge-weight properties
+
Required output
=
Shortest-path algorithm
22. Dijkstra’s Algorithm
For non-negative edge weights, Dijkstra repeatedly selects the currently closest unprocessed node.
The conceptual structure is:
Start
↓
Best known distance
↓
Choose minimum-distance node
↓
Relax outgoing edges
↓
Update distances
↓
Repeat
A priority queue is the natural implementation tool.
Python:
import heapq
def dijkstra(graph, start):
distances = {node: float("inf") for node in graph}
distances[start] = 0
heap = [(0, start)]
while heap:
distance, node = heapq.heappop(heap)
if distance != distances[node]:
continue
for neighbor, weight in graph[node]:
new_distance = distance + weight
if new_distance < distances[neighbor]:
distances[neighbor] = new_distance
heapq.heappush(heap, (new_distance, neighbor))
return distances
23. Why Dijkstra Needs Non-Negative Weights
Dijkstra permanently relies on the fact that once the smallest-distance node is selected, a later path cannot make it cheaper through a negative edge.
Negative edges break that assumption.
This is a classic example of:
Algorithm
+
Precondition
A Staff-level engineer should always state the precondition.
24. Graph Search as State-Space Search
A powerful abstraction is that a graph does not always represent physical objects.
Nodes can represent states.
For example:
Chess position
+
Possible move
=
Graph edge
Or:
Configuration
+
Valid transition
=
Graph edge
Then:
Start state
↓
Transitions
↓
Goal state
This turns many apparently unrelated problems into graph-search problems.
25. Grid Problems Are Often Graph Problems
Consider:
0 0 1
0 1 0
0 0 0
Each cell can be treated as a node.
Adjacent cells become edges.
Therefore:
Grid
↓
Implicit graph
↓
BFS / DFS
This is one of the most valuable interview transformations.
You don’t necessarily need to explicitly build the graph.
Instead, generate neighbors dynamically.
26. Implicit Graphs
An implicit graph is one where:
nodes are states
edges are generated by rules
Examples:
word transformations
maze movement
puzzle states
robot positions
game states
configuration transitions
The graph exists conceptually even if no adjacency-list object exists in memory.
27. Graphs vs Trees
A tree is a special kind of graph.
A typical tree has:
one connected component
no cycles
Graphs are more general:
cycles
multiple components
directed edges
weighted edges
arbitrary connectivity
The source’s pattern map separates hierarchical structures from general connections, associating hierarchies with trees and connections with graphs.
28. Graph Modeling Checklist
When you suspect a graph, ask:
□ What are the nodes?
□ What are the edges?
□ Directed or undirected?
□ Weighted or unweighted?
□ Can cycles exist?
□ Is the graph connected?
□ Is the graph sparse or dense?
□ Do I need shortest path?
□ Do I need reachability?
□ Do I need ordering?
□ Do I need connected components?
□ Do I need cycle detection?
□ Is the graph explicit or implicit?
This prevents many implementation mistakes.
29. Staff-Level Pattern Selection
Use:
Need reachability?
→ DFS / BFS
Need shortest unweighted path?
→ BFS
Need connected components?
→ DFS / BFS
Need dependency ordering?
→ Topological sort
Need cycle detection?
→ DFS / indegree reasoning
Need shortest non-negative weighted path?
→ Dijkstra
Need state-space exploration?
→ BFS / DFS
Need hierarchical structure?
→ Tree algorithms
But always validate the constraints and graph properties first.
The source emphasizes exactly this broader principle:
Pattern
+
Constraints
+
Requirements
=
Algorithm choice
30. Brute Force → Graph Modeling
A strong interview approach is:
Brute force
↓
Understand repeated work
↓
Identify entities
↓
Identify relationships
↓
Model as graph
↓
Choose traversal
↓
Optimize
This follows the broader iterative-refinement approach emphasized in the source.
31. Concrete Examples First
Before coding, draw a tiny graph.
For example:
A ─ B ─ D
│
C ──────┘
Then ask:
What should BFS do?
What should DFS do?
What is the shortest path?
What happens if I add a cycle?
What happens if D is unreachable?
The source recommends using concrete examples—including small and extreme inputs—before generalizing.
32. Graph Edge Cases
Always test:
empty graph
single node
single edge
disconnected graph
self-loop
duplicate edges
cycle
no path
multiple paths
very large graph
dense graph
sparse graph
For directed graphs also test:
DAG
directed cycle
multiple incoming edges
multiple outgoing edges
33. Python Tools for Graph Problems
The source emphasizes fluency with standard Python data structures and specifically associates:
list
set
deque
heapq
with common algorithmic patterns.
For graphs:
set
→ visited
deque
→ BFS
list
→ adjacency lists / DFS stack
heapq
→ priority queues / Dijkstra
The implementation should make the algorithm obvious.
34. Recursion vs Iteration
DFS is often written recursively:
def dfs(node):
...
But recursion depth matters.
The source explicitly warns that recursion stack space counts toward complexity and can become O(n) for a skewed structure.
Therefore, for very large graphs, an iterative DFS may be safer.
35. Complexity Is Part of the Answer
Don’t finish with:
“This should be fast.”
Say:
There are V vertices and E edges.
Each vertex is visited once.
Each edge is examined at most a constant number of times.
Therefore:
Time = O(V + E)
Then discuss:
visited set = O(V)
queue/stack = O(V)
This is the level of precision expected in a strong Staff interview.
36. Graph Interview Communication
The source strongly recommends thinking aloud while exposing decisions rather than narrating every keystroke.
For a graph problem, communicate:
"I'll model each service as a node."
"An allowed dependency becomes a directed edge."
"Because I need the minimum number of transitions,
I'll use BFS."
"I'll mark a node visited when I enqueue it,
so it cannot be inserted repeatedly."
"The traversal is O(V + E)."
That is much stronger than silently writing code.
37. The Staff-Level Graph Mental Model
When a problem appears complicated, ask:
What are the entities?
↓
What relationships connect them?
↓
Can those relationships be edges?
↓
What question are we asking?
↓
Reachability?
Shortest path?
Connectivity?
Cycle?
Ordering?
↓
Choose the graph algorithm.
This transforms a vague problem into a structured algorithmic problem.
38. Part 17 → Part 18
The progression now becomes:
Part 15 — Recursion
↓
Decompose problems
Part 16 — Dynamic Programming
↓
Reuse overlapping subproblems
Part 17 — Greedy Algorithms
↓
Prove safe local choices
Part 18 — Graphs
↓
Model relationships and connectivity
The available source explicitly describes Parts/Chapters 15–18 as the advanced algorithmic portion and identifies graphs as part of that progression.
39. Graph Cheat Sheet
| Problem | Typical technique |
|---|---|
| Reachability | DFS / BFS |
| Connected components | DFS / BFS |
| Shortest unweighted path | BFS |
| Cycle detection | DFS / indegree |
| Dependency ordering | Topological sort |
| Non-negative weighted shortest path | Dijkstra |
| Grid traversal | BFS / DFS |
| State-space search | BFS / DFS |
| Dense graph | Consider matrix |
| Sparse graph | Usually adjacency list |
40. Final Staff-Level Checklist
Before submitting a graph solution, ask:
□ Did I identify the nodes correctly?
□ Did I identify the edges correctly?
□ Directed or undirected?
□ Weighted or unweighted?
□ Can cycles exist?
□ Did I choose BFS or DFS for a reason?
□ Did I maintain visited state?
□ Is the graph explicit or implicit?
□ Did I handle disconnected components?
□ Did I test no-path cases?
□ Did I state time complexity?
□ Did I state auxiliary space?
□ Did I account for recursion depth?
□ Did I explain the invariant?
□ Can I explain why the algorithm is correct?
41. Final Takeaway
Graph problems become much easier when you stop seeing them as isolated coding questions.
Instead:
Entities
↓
Relationships
↓
Graph model
↓
Graph properties
↓
Algorithm
↓
Traversal
↓
Correctness
↓
Complexity
The most valuable Staff-level skill is graph modeling.
Once the model is correct, the algorithm often becomes much easier to see.
And the broader interview principle from the source still applies:
Pattern
+
Constraints
+
Requirements
=
Algorithm choice
That is the mindset that turns graph problems from memorization exercises into structured engineering problems.
Source Note
This article is grounded in the available uploaded interview-preparation material. The material explicitly places Graphs in the advanced algorithmic sequence (Parts/Chapters 15–18), identifies graph modeling as an algorithmic pattern, and connects graphs with relationships/connections and DFS/BFS.
The available uploaded extraction does not expose the complete original book’s numbered Graph problem set. Therefore, this Part 18 article presents the graph concepts that are supported by the available material and clearly distinguishes the broader graph tutorial structure from source-specific numbered problems.