Hello! Welcome to the first lesson in our new module on Graphs.
In our last lesson, you saw how a maze problem could be viewed as a graph, where each cell is a node and a valid move is an edge. We used backtracking, a form of Depth-First Search, to find paths through it. Today, we'll formalize this concept by learning how to represent these complex, interconnected data structures in code.
This lesson directly addresses the learning outcome: Represent graphs using an adjacency matrix and an adjacency list. We'll explore these two fundamental methods, analyze their trade-offs, and see how to implement them in Java, setting the stage for all the graph algorithms to come.
1. What is a Graph?
At its core, a graph is a data structure used to model relationships. It consists of two main components:
- Vertices (or Nodes): The fundamental entities within the graph. Think of these as cities on a map, people in a social network, or devices in a computer network.
- Edges: The connections between pairs of vertices. These represent roads, friendships, or network cables.
Before we dive into representing them, it's crucial to understand a few key properties that define a graph's behavior. The happycoding.io article provides an excellent introduction to these concepts.
To start, please read this brief introduction to graphs, which covers the essential terminology we'll be using.
Read the sections titled 'Graphs', 'Connection Costs', and 'Direction'. Focus on understanding the difference between: Undirected vs. Directed graphs: Are connections two-way or one-way? Unweighted vs. Weighted graphs: Do edges simply exist, or do they have an associated cost/weight?
Understanding these distinctions is vital because your choice of representation will depend on them. For example, representing a directed, weighted graph is different from representing a simple, unweighted, undirected graph.
2. Representation 1: Adjacency Matrix
The first and most straightforward way to represent a graph is using an adjacency matrix. This is a 2D array, typically of size V x V (where V is the number of vertices).
The rules are simple:
matrix[i][j] = 1if there is an edge from vertexito vertexj.matrix[i][j] = 0if there is no edge.
For a weighted graph, instead of storing 1, you store the weight of the edge. A 0 or infinity can represent the absence of an edge.
For an undirected graph, the matrix will be symmetric across the main diagonal (since an edge from i to j implies an edge from j to i).

This video provides a great walkthrough of the concept and its implementation.
G-3. Graph Representation in Java | Two Ways to Represent
This video, 'Graph Representation in Java', from the 'take U forward' channel, clearly explains the adjacency matrix, its space complexity, and how to populate it based on typical problem inputs.
Watch from 02:08 to 05:23. Pay attention to how an edge (u, v) in an undirected graph leads to two entries in the matrix (adj[u][v] = 1 and adj[v][u] = 1).
Pros and Cons of Adjacency Matrix
- Pros:
- Fast Edge Lookup: Checking if an edge exists between two vertices
iandjis anO(1)operation (just look upmatrix[i][j]).
- Fast Edge Lookup: Checking if an edge exists between two vertices
- Cons:
- High Space Complexity: It requires
O(V^2)space, regardless of the number of edges. This is very inefficient for sparse graphs (graphs with many vertices but few edges), which are common in practice. - Slow Neighbor Iteration: To find all neighbors of a vertex, you must iterate through its entire row, which takes
O(V)time.
- High Space Complexity: It requires
3. Representation 2: Adjacency List
The adjacency list is the most common way to represent graphs in competitive programming and technical interviews, primarily because it's more space-efficient for sparse graphs.
The concept is an array of lists. The list at index i stores all the vertices that are adjacent to vertex i.
In Java, this is typically implemented as:
ArrayList<ArrayList<Integer>> adj;- Or, more flexibly,
Map<Integer, List<Integer>> adj;

Let's return to the video from 'take U forward' to see how this works.
G-3. Graph Representation in Java | Two Ways to Represent
The same video also provides an excellent explanation of the adjacency list representation, directly comparing it to the matrix.
Now watch from 05:23 to 09:31. Notice how the space complexity is related to the number of edges (M), making it much more efficient. Also, observe how to handle directed and weighted graphs.
For weighted graphs, you can't just store a list of integers. Instead, you'd store a list of objects or pairs, where each element contains the neighbor and the weight of the edge to it. A common Java implementation would be ArrayList<ArrayList<Pair<Integer, Integer>>>.
Pros and Cons of Adjacency List
- Pros:
- Space Efficient: Requires
O(V + E)space (whereEis the number of edges). This is significantly better thanO(V^2)for sparse graphs. - Fast Neighbor Iteration: Iterating through all neighbors of a vertex
uis efficient, as you only traverse the list of its actual neighbors.
- Space Efficient: Requires
- Cons:
- Slower Edge Lookup: To check if an edge exists between
uandv, you have to search throughu's adjacency list, which can take up toO(degree(u))time in the worst case (wheredegree(u)is the number of neighbors).
- Slower Edge Lookup: To check if an edge exists between
Test your understanding!
You are tasked with modeling the entire flight network of a major airline. There are thousands of airports (vertices), but any given airport only has direct flights (edges) to a few dozen other airports.
Which representation, adjacency matrix or adjacency list, would be more suitable and why?
Show answer
An adjacency list would be far more suitable. The graph is sparse: the number of vertices V (airports) is large, but the number of edges E (flights) is relatively small compared to the maximum possible number of edges (V^2).
- Using an adjacency matrix would require
V^2space, which would be enormous and mostly filled with zeros (representing no direct flight). - An adjacency list would only use
O(V + E)space, which is much more memory-efficient in this scenario.
4. Implementation in Java
In most programming challenges, you are given the graph as a list of edges. A common first step is to convert this input into a more usable format like an adjacency list.
A typical input format looks like this:
- The first line contains two integers:
N(number of vertices) andM(number of edges). - The next
Mlines each contain two integers,uandv, representing an edge between vertexuand vertexv.
The happycoding.io resource has an excellent section showing how to perform this conversion.
Graphs - Converting Input Data
This section of the 'happycoding.io' article is highly practical. It shows concrete Java code for converting a list of edges into both an adjacency matrix and an adjacency list.
Focus on the section 'Converting Input Data'. Study the code snippets for building an adjacency matrix and, more importantly, for building an adjacency list. This is a fundamental pattern you will use repeatedly.
Here's a concise example of how to build an adjacency list for an undirected graph in Java, based on the pattern you just reviewed:
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
public class GraphRepresentation {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.println("Enter number of vertices (N) and edges (M):");
int n = sc.nextInt(); // Number of vertices
int m = sc.nextInt(); // Number of edges
// Adjacency List: An ArrayList of ArrayLists.
// We use n+1 size for 1-based indexing, which is common in problems.
ArrayList<ArrayList<Integer>> adj = new ArrayList<>(n + 1);
// Initialize an empty list for each vertex.
for (int i = 0; i <= n; i++) {
adj.add(new ArrayList<>());
}
System.out.println("Enter " + m + " edges (u v):");
for (int i = 0; i < m; i++) {
int u = sc.nextInt();
int v = sc.nextInt();
// Since it's an undirected graph, add an edge from u to v
adj.get(u).add(v);
// AND an edge from v to u
adj.get(v).add(u);
}
// Print the adjacency list to verify
System.out.println("\nAdjacency List:");
for (int i = 1; i <= n; i++) {
System.out.print("Vertex " + i + ": ");
for (Integer neighbor : adj.get(i)) {
System.out.print(neighbor + " ");
}
System.out.println();
}
sc.close();
}
}
This code snippet is a template you can adapt for many graph problems. For a directed graph, you would simply omit the adj.get(v).add(u); line.
Conclusion
You've now taken your first and most crucial step into the world of graphs. Understanding how to represent them is the foundation upon which all other graph algorithms are built.
Key Takeaways:
- A graph is defined by its vertices and edges, and can be directed/undirected and weighted/unweighted.
- An Adjacency Matrix uses
O(V^2)space and offersO(1)edge lookups. It is best for dense graphs whereEis close toV^2. - An Adjacency List uses
O(V + E)space and is efficient for iterating over neighbors. It is the preferred choice for sparse graphs and most competitive programming problems. - You should be comfortable converting a standard edge-list input into an adjacency list representation in Java.
Preview of the Next Lesson:
Now that we know how to store a graph, how do we explore it? In the next lesson, we will learn our first formal graph traversal algorithm: Breadth-First Search (BFS). We'll use the adjacency list you learned to build today to implement BFS, which explores the graph level by level and is the key to finding the shortest path in unweighted graphs.