📘 Introduction to Graph Theory
🧠 What is a Graph?
A graph is a non-linear data structure consisting of vertices (nodes) connected by edges (links). Graphs model relationships and connections between entities, making them fundamental for solving real-world problems like social networks, maps, dependencies, and more.
Think of a graph as a network where dots (vertices) are connected by lines (edges).
🎯 Why Learn Graph Theory?
Graphs are everywhere in computer science and the real world:
- 🌐 Social Networks — Friends, followers, connections
- 🗺️ Maps & Navigation — Cities connected by roads
- 🔗 Web Pages — Links between websites
- 📦 Dependencies — Package managers, build systems
- 🧬 Biology — Protein interactions, neural networks
- 💼 Recommendation Systems — Product/content suggestions
🛠️ Graph Basics
✅ Key Terminology
| Term | Definition | Example |
|---|---|---|
| Vertex (Node) | A point in the graph | Cities in a map |
| Edge | Connection between two vertices | Road between cities |
| Degree | Number of edges connected to a vertex | Number of roads from a city |
| Path | Sequence of vertices connected by edges | Route from A to B |
| Cycle | Path that starts and ends at same vertex | Circular route |
| Connected Graph | Every vertex is reachable from any other | All cities are accessible |
✅ Types of Graphs
1️⃣ Directed vs Undirected
Undirected Graph: Edges have no direction (bidirectional)
A ←→ B
Example: Facebook friendships (mutual)
Directed Graph (Digraph): Edges have direction
A → B
Example: Twitter follows (one-way)
2️⃣ Weighted vs Unweighted
Unweighted Graph: All edges are equal
A — B — C
Weighted Graph: Edges have values (costs, distances)
A —5— B —3— C
Example: Road network with distances
3️⃣ Cyclic vs Acyclic
Cyclic Graph: Contains at least one cycle
A → B → C → A
Acyclic Graph: No cycles
A → B → C
DAG (Directed Acyclic Graph): Directed graph with no cycles — used in task scheduling, dependency resolution
💾 Graph Representation
1️⃣ Adjacency Matrix
A 2D array where matrix[i][j] = 1 if there’s an edge from vertex i to vertex j.
# Graph: 0 — 1 — 2
# | |
# 3 ——————
graph = [
[0, 1, 0, 1], # 0 connects to 1, 3
[1, 0, 1, 0], # 1 connects to 0, 2
[0, 1, 0, 1], # 2 connects to 1, 3
[1, 0, 1, 0] # 3 connects to 0, 2
]
Pros: ✅ Fast edge lookup \(O(1)\)
Cons: ❌ Space inefficient \(O(V^2)\), slow to iterate neighbors
2️⃣ Adjacency List
A dictionary/array where each vertex stores a list of its neighbors.
# Same graph as above
graph = {
0: [1, 3],
1: [0, 2],
2: [1, 3],
3: [0, 2]
}
Pros: ✅ Space efficient \(O(V + E)\), fast neighbor iteration
Cons: ❌ Slower edge lookup \(O(degree)\)
👉 Most interview problems use adjacency lists!
3️⃣ Edge List
A list of all edges in the graph.
edges = [(0, 1), (1, 2), (2, 3), (0, 3)]
Use case: Useful for algorithms like Kruskal’s MST
🔍 Basic Graph Operations
| Operation | Adjacency Matrix | Adjacency List |
|---|---|---|
| Add vertex | \(O(V^2)\) | \(O(1)\) |
| Add edge | \(O(1)\) | \(O(1)\) |
| Remove vertex | \(O(V^2)\) | \(O(V + E)\) |
| Remove edge | \(O(1)\) | \(O(V)\) |
| Check if edge exists | \(O(1)\) | \(O(V)\) |
| Find all neighbors | \(O(V)\) | \(O(1)\) |
🚀 Core Graph Algorithms
🔹 1. Graph Traversal
Purpose: Visit all vertices in a graph
Breadth-First Search (BFS)
- Explores level by level (like ripples in water)
- Uses a queue
- Use cases: Shortest path in unweighted graphs, level-order traversal
from collections import deque
def bfs(graph, start):
visited = set()
queue = deque([start])
visited.add(start)
while queue:
vertex = queue.popleft()
print(vertex, end=' ')
for neighbor in graph[vertex]:
if neighbor not in visited:
visited.add(neighbor)
queue.append(neighbor)
Time Complexity: \(O(V + E)\)
Depth-First Search (DFS)
- Explores as deep as possible before backtracking
- Uses a stack (or recursion)
- Use cases: Cycle detection, topological sort, connected components
def dfs(graph, vertex, visited=None):
if visited is None:
visited = set()
visited.add(vertex)
print(vertex, end=' ')
for neighbor in graph[vertex]:
if neighbor not in visited:
dfs(graph, neighbor, visited)
return visited
Time Complexity: \(O(V + E)\)
🔹 2. Shortest Path Algorithms
Dijkstra’s Algorithm
- Finds shortest path from source to all vertices
- Works with non-negative weights
- Uses priority queue (min-heap)
Time Complexity: \(O((V + E) \log V)\)
Bellman-Ford Algorithm
- Handles negative weights
- Detects negative cycles
- Slower than Dijkstra
Time Complexity: \(O(V \times E)\)
Floyd-Warshall Algorithm
- Finds shortest paths between all pairs of vertices
- Uses dynamic programming
Time Complexity: \(O(V^3)\)
🔹 3. Minimum Spanning Tree (MST)
Goal: Connect all vertices with minimum total edge weight
Kruskal’s Algorithm
- Sort edges by weight
- Add edges if they don’t create a cycle
- Uses Union-Find data structure
Time Complexity: \(O(E \log E)\)
Prim’s Algorithm
- Start from a vertex
- Greedily add minimum weight edge
- Uses priority queue
Time Complexity: \(O((V + E) \log V)\)
🔹 4. Topological Sort
Purpose: Linear ordering of vertices in a DAG
Use cases: Task scheduling, course prerequisites, build systems
def topological_sort(graph):
visited = set()
stack = []
def dfs(vertex):
visited.add(vertex)
for neighbor in graph[vertex]:
if neighbor not in visited:
dfs(neighbor)
stack.append(vertex)
for vertex in graph:
if vertex not in visited:
dfs(vertex)
return stack[::-1] # Reverse to get correct order
Time Complexity: \(O(V + E)\)
🔹 5. Cycle Detection
In Undirected Graph: Use DFS with parent tracking
In Directed Graph: Use DFS with recursion stack (detect back edges)
🔹 6. Connected Components
Purpose: Find groups of connected vertices
def count_components(graph):
visited = set()
count = 0
def dfs(vertex):
visited.add(vertex)
for neighbor in graph[vertex]:
if neighbor not in visited:
dfs(neighbor)
for vertex in graph:
if vertex not in visited:
dfs(vertex)
count += 1
return count
🧪 Example Problem: Number of Islands
Problem: Given a 2D grid of ‘1’s (land) and ‘0’s (water), count the number of islands.
def numIslands(grid):
if not grid:
return 0
rows, cols = len(grid), len(grid[0])
visited = set()
count = 0
def dfs(r, c):
if (r < 0 or r >= rows or c < 0 or c >= cols or
grid[r][c] == '0' or (r, c) in visited):
return
visited.add((r, c))
# Explore 4 directions
dfs(r + 1, c)
dfs(r - 1, c)
dfs(r, c + 1)
dfs(r, c - 1)
for r in range(rows):
for c in range(cols):
if grid[r][c] == '1' and (r, c) not in visited:
dfs(r, c)
count += 1
return count
🎯 Common Graph Patterns
| Pattern | When to Use | Example Problems |
|---|---|---|
| BFS | Shortest path, level-order | Word Ladder, Rotting Oranges |
| DFS | Explore all paths, backtracking | Number of Islands, Course Schedule |
| Union-Find | Connected components, cycles | Number of Provinces, Redundant Connection |
| Topological Sort | Dependency ordering | Course Schedule II, Alien Dictionary |
| Dijkstra | Weighted shortest path | Network Delay Time, Cheapest Flights |
🕵️ How to Identify Graph Problems
Look for these keywords:
- “Connected”, “Network”, “Path”, “Route”
- “Dependencies”, “Prerequisites”, “Order”
- “Islands”, “Regions”, “Components”
- “Shortest”, “Minimum”, “Optimal”
- “Cycle”, “Loop”, “Circular”
💡 Problem-Solving Strategy
- Identify the graph structure
- What are the vertices? What are the edges?
- Is it directed or undirected?
- Is it weighted or unweighted?
- Choose representation
- Usually adjacency list for interviews
- Select algorithm
- Traversal? → BFS/DFS
- Shortest path? → BFS (unweighted) or Dijkstra (weighted)
- Ordering? → Topological sort
- Components? → Union-Find or DFS
- Handle edge cases
- Empty graph, disconnected components, cycles
📊 Time Complexity Cheat Sheet
| Algorithm | Time Complexity | Space Complexity |
|---|---|---|
| BFS | \(O(V + E)\) | \(O(V)\) |
| DFS | \(O(V + E)\) | \(O(V)\) |
| Dijkstra | \(O((V + E) \log V)\) | \(O(V)\) |
| Bellman-Ford | \(O(V \times E)\) | \(O(V)\) |
| Floyd-Warshall | \(O(V^3)\) | \(O(V^2)\) |
| Kruskal’s MST | \(O(E \log E)\) | \(O(V)\) |
| Prim’s MST | \(O((V + E) \log V)\) | \(O(V)\) |
| Topological Sort | \(O(V + E)\) | \(O(V)\) |
✍️ Practice Assignments
- Implement BFS and DFS for a given graph
- Find if a path exists between two vertices
- Detect a cycle in an undirected graph
- Find the shortest path in an unweighted graph
- Implement topological sort for a DAG
📝 Suggested Practice Problems
Easy
Medium
Hard
🔔 Next: Dive deeper into specific graph algorithms and solve real-world problems!