Graph
Graph Representation
Adjacency Matrix
- A 2D V×V array (
matrix[i][j]) where:1(or weight) → edge exists between vertexiandj.0→ no edge.
- Works for directed/undirected, weighted/unweighted graphs.
Pros:
- Fast edge check:
O(1)to check if edge(u, v)exists. - Simple implementation: Very straightforward.
- Good for dense graphs: When number of edges E ≈ V^2.
Cons:
- Memory heavy: Requires O(V2) space, even if sparse.
- Inefficient for sparse graphs: Most of the matrix might be zeros.
- Slow to iterate neighbors: Takes O(V) to find all neighbors of a vertex.
When to use: Small graphs or dense graphs where checking edge existence quickly is needed.
Adjacency List
- Each vertex has a list of neighbors.
- Usually implemented as an array of lists or vectors.
- Can store weights if needed.
Pros:
- Space efficient: Uses O(V+E) space, good for sparse graphs.
- Easy to iterate neighbors: Only iterate over actual edges, not empty slots.
- Flexible: Can store weights easily.
Cons:
- Edge existence check is slower: Need to search the list → O(k), where k = degree of vertex.
- Slightly more complex implementation than a matrix.
When to use: Large sparse graphs, most real-world networks (social networks, maps).
Edge List
A list of all edges. Each edge is stored as a pair (u, v) or (u, v, w) if weighted.
Pros:
- Simple and compact: Good for storing edges only.
- Easy to implement algorithms like Kruskal’s (MST).
Cons:
- Slow to check edge existence: O(E) search needed.
- Hard to iterate neighbors of a vertex efficiently.
When to use: Algorithms that process edges directly, e.g., Kruskal’s MST, or input/output of edges.
Adjacency Set / Map
- Similar to adjacency list but uses hash sets/maps instead of lists.
- Example:
unordered_map<int, unordered_set<int>>in C++.
Pros:
- Fast edge check: O(1) average for set/map.
- Space efficient for sparse graphs.
Cons:
- More memory overhead than simple lists.
- Slightly slower iteration than plain arrays/lists.
When to use: Sparse graphs when you need fast existence checks for edges.
Incidence Matrix
- A V×E matrix:
matrix[i][j] = 1if vertexiis incident to edgej. - Rarely used except in special graph algorithms.
Pros:
- Mathematically elegant; good for theoretical analysis.
- Can represent multi-graphs easily.
Cons:
- Memory heavy: O(V×E)
- Not practical for most programming problems.
- Edge iteration and neighbor finding is slower.
When to use: Academic or theoretical problems, multi-graphs, or algorithms needing incidence info.
Quick Comparison
| Representation | Space Complexity | Edge Check | Neighbor Iteration | Best For |
|---|---|---|---|---|
| Adjacency Matrix | O(V²) | O(1) | O(V) | Dense graph, fast edge check |
| Adjacency List | O(V+E) | O(k) | O(k) | Sparse graph, iterate neighbors |
| Edge List | O(E) | O(E) | O(E) | Edge-centric algorithms |
| Adjacency Set/Map | O(V+E) | O(1) avg | O(k) | Sparse graph, fast edge check |
| Incidence Matrix | O(V*E) | O(V) | O(E) | Multi-graphs, theoretical work |
Graph Implementation
C Programming
struct Node {
int vertex;
struct Node* next;
};
// Graph structure
struct Graph {
int numVertices;
struct Node** adjLists;
bool* visited;
};
// Create a node
struct Node* createNode(int v) {
struct Node* newNode = malloc(sizeof(struct Node));
newNode->vertex = v;
newNode->next = NULL;
return newNode;
}
// Create a graph
struct Graph* createGraph(int vertices) {
struct Graph* graph = malloc(sizeof(struct Graph));
graph->numVertices = vertices;
graph->adjLists = malloc(vertices * sizeof(struct Node*));
graph->visited = malloc(vertices * sizeof(bool));
for (int i = 0; i < vertices; i++) {
graph->adjLists[i] = NULL;
graph->visited[i] = false;
}
return graph;
}
// Add edge (undirected)
void addEdge(struct Graph* graph, int src, int dest) {
// Add edge src -> dest
struct Node* newNode = createNode(dest);
newNode->next = graph->adjLists[src];
graph->adjLists[src] = newNode;
// Add edge dest -> src
newNode = createNode(src);
newNode->next = graph->adjLists[dest];
graph->adjLists[dest] = newNode;
}
int main() {
struct Graph* graph = createGraph(4);
addEdge(graph, 0, 1);
addEdge(graph, 0, 2);
addEdge(graph, 1, 2);
addEdge(graph, 2, 3);
}
C++ Programming
class Graph {
int V; // Number of vertices
vector<list<int>> adj; // Adjacency list
public:
Graph(int V) {
this->V = V;
adj.resize(V);
}
// Add edge (undirected)
void addEdge(int v, int w) {
adj[v].push_back(w);
adj[w].push_back(v);
}
};
int main() {
Graph g(4);
g.addEdge(0, 1);
g.addEdge(0, 2);
g.addEdge(1, 2);
g.addEdge(2, 3);
}
Traversing
DFS
- Recursive/Iterative: Required 3 parameter(adj list, visited nodes, current node)
- Itertive: In stack which element insert first, pop out at last, which reverse the order. So iterate the list from last and insert into stack, whish will pop out at last as well, this way the order of traversing maintain properly.
Characterstics:
- It is useful for tasks like cycle detection, topological sorting, and maze solving.
- Find path without shortest path requirement
BFS
- Iterative: Just replace stack with queue in dfs, rest is same.
Characterstics:
- Explores neighbors before children(layer by layer)
- Finds shortest path in an unweighted graph
When to Use BFS vs DFS?
| Aspect | BFS | DFS |
|---|---|---|
| Shortest Path? | ✅ Yes (unweighted graphs) | ❌ No |
| Memory Usage | 🟠 High (stores full layer) | 🟢 Lower (stores one path) |
| Graph Type? | 🟢 Wide, shallow graphs | 🟢 Deep, narrow graphs |
| Cycle Detection? | 🟢 Yes | 🟢 Yes |
| Connected Components? | 🟢 Yes | 🟢 Yes |
Complexity
- V: Number of vertices (nodes)
- E: Number of edges (connections)
Time Complexity: O(V + E)
Space Complexity: O(V)
Shortest path using BFS
- BFS explores all nodes at distance 1 first, then distance 2, and so on.
- The first time you reach a node, you have found the shortest path from the source.
- To extract the path, you need to track the parent of each node.
- Create a
visitedarray to mark visited nodes. - Create a
parentarray to store the predecessor of each node.parent[v] = uthe node from which you reachedv.