Graphs
A graph is a data structure that consists of vertices (or “nodes”) and links between these vertices, called “edges.” Edges can be directed or undirected, depending on the application. Graph representations of data have many practical use cases; in social networks we can represent people as vertices and friendships as undirected edges, and for maps and GPS navigation, we can represent intersections as vertices and road segments as directed edges.
Graphs help solve problems involving:
- Connectivity (paths, components)
- Shortest paths
- Cycles
- Topological ordering
- Network flows
- Minimum spanning trees
- Search queries (DFS/BFS)
- Routing and navigation
They are foundational in interviews involving dynamic systems, networks, and traversal problems.
Types of graphs
1. Undirected graph
Edges do not have a specific direction, meaning the connection between two vertices is bidirectional.
Used in:
- Friend networks
- Road networks (two-way)
- Component detection
2. Directed graph (Digraph)
Edges are unidirectional, with a defined source and destination vertex. A → B is not the same as B → A.
Used in:
- Dependencies
- Web links
- One-way systems
3. Weighted graph
Each edge has an associated value, or "weight," representing a cost, distance, or time.
Used in:
- Dijkstra's algorithm
- Minimum spanning tree
- Navigation systems
4. Unweighted graph
All edges are treated equally, with no associated weights. Used for shortest path via BFS.
5. Cyclic & acyclic
Cyclic graph: Contains at least one cycle, which is a path that starts and ends at the same vertex.
Acyclic graph: A graph with no cycles. A Directed Acyclic Graph (DAG) is a common example.
6. Connected vs disconnected
Connected: Every pair of vertices has a path
Disconnected: Some nodes are isolated
Graph representations
1. Adjacency list (Most common)
An adjacency list stores each node along with a list of its neighbors. It is the most memory-efficient representation for sparse graphs.
Pythongraph = { 0: [1, 2], 1: [2], 2: [0, 3], 3: [3] }
Characteristics
- Uses a list/array of lists (or hash map of lists).
- Great for BFS & DFS.
- Space:
O(V + E).
2. Adjacency matrix
A 2D matrix V × V where matrix[u][v] = 1 (or weight) if an edge exists.
Python[[0 1 1 0] [0 0 1 0] [1 0 0 1] [0 0 0 1]]
Characteristics
- Fast edge lookup:
O(1) - Space:
O(V²)— not memory-friendly for large graphs - Works well for dense graphs
- Used for weighted graphs too
3. Edge list
A simple list containing all edges as pairs (or triples when weighted).
Pythonedges = [ (0, 1), (0, 2), (1, 2), (2, 3), (3, 3) ]
Characteristics
- Most compact representation
- Great when you only need edges (e.g., Kruskal’s algorithm)
- Space:
O(E)

Calculating memory usage
The memory used by a graph is a function of the number of vertices V and the number of edges E. For an adjacency list implementation, the total space usage can be expressed as V + E, where edges are IDs or pointers. For an adjacency matrix, the memory usage is V^2 since the state of all edges is stored.
Common edge cases & pitfalls
-
Not checking for cycles
-
Running DFS/BFS without a visited set will lead to infinite loops
-
Incorrectly treating a directed graph as undirected
-
Confusing adjacency matrix with adjacency list indexing
-
Not handling disconnected graphs (need to run traversal multiple times)
-
Using BFS for weighted graph shortest path (should use Dijkstra)
-
Using DFS for shortest path (not guaranteed)
Quiz
- BFS is best for finding shortest path in which type of graph?
a) Weighted
b) Negative weighted
c) Unweighted
d) Cyclic
- What structure does DFS use internally?
a) Queue
b) Stack
c) Heap
d) Hash map
- Topological sort only works on:
a) Trees
b) DAGs
c) Weighted graphs
d) Cyclic graphs