Graphs are one of the most frequently asked topics in interviews because they can encapsulate multiple concepts in a single question. To be good at graph problems, it’s essential to have strong fundamentals. After I started covering the LeetCode patterns I believe are important to know in the LeetCode Cheatsheet for Coding Interviews post, now it’s time to focus on graph problems and patterns. Given their importance, I decided to dedicate a separate post to graphs. As mentioned in my previous blog, I use Python for my interviews, so if you do too—or are considering it—I recommend checking out my Python for Interviews Cheatsheet.
Graph Representation
Graph representation is crucial as it affects the efficiency of graph algorithms. The two primary ways to represent graphs are:
Adjacency Matrix: A 2D array where each cell (i, j) indicates the presence (and possibly weight) of an edge between vertices i and j.
Pros: Quick edge lookup (O(1) time).
Cons: Consumes O(V^2) space, inefficient for sparse graphs.
Adjacency List: An array of lists where each index represents a vertex, and each element in the list represents its adjacent vertices.
Pros: Space-efficient for sparse graphs (O(V + E) space).
Cons: Edge lookup can take O(V) time in the worst case.
Template
Adjacency List in Python:
also, creating a dictionary that holds lists is a possible implementation
Adjacency Matrix in Python:
Graph Traversal
Depth-First Search (DFS)
Idea
Depth-First Search (DFS) explores as far as possible along each branch before backtracking. It uses a stack data structure, either implicitly with recursion or explicitly, to keep track of the vertices to be explored next. DFS is useful for:
Given an m x n matrix of non-negative integers representing the height of each unit cell in a continent, find the list of grid coordinates where water can flow to both the Pacific and Atlantic oceans. Water can flow from a cell to its neighboring cells (up, down, left, or right) if the neighboring cell’s height is less than or equal to the current cell’s height.
Solution:
We can perform DFS from the cells adjacent to each ocean. Cells that can reach both oceans are the intersections of the cells visited from both DFS traversals.
Breadth-First Search (BFS)
Idea
Breadth-First Search (BFS) explores the neighbor nodes first before moving to the next level neighbors. It uses a queue to keep track of the nodes to be explored next. BFS is useful for:
Every minute, any fresh orange that is adjacent (4-directionally) to a rotten orange becomes rotten. Return the minimum number of minutes that must elapse until no cell has a fresh orange. If this is impossible, return -1.
Solution:
We can use BFS to model the spread of rot from rotten oranges to fresh ones.
Topological Sort
Idea
Topological sorting is the linear ordering of vertices in a directed acyclic graph (DAG) such that for every directed edge uv, vertex u comes before v in the ordering. It’s used for:
There are n courses labeled from 0 to n-1. Some courses have prerequisites. Return the ordering of courses you should take to finish all courses. If it’s impossible, return an empty array.
Solution:
We can model courses and prerequisites as a graph and perform topological sorting using DFS.
Shortest Path Algorithms
Dijkstra’s Algorithm
Idea
Dijkstra’s algorithm finds the shortest paths from a source vertex to all other vertices in a weighted graph with non-negative weights.
Given a list of travel times as directed edges times, where times[i] = (u, v, w), send a signal from a node K to all nodes in the network. Return how long it takes for all nodes to receive the signal. If it’s impossible, return -1.
Solution:
We can use Dijkstra’s algorithm to find the shortest time to reach all nodes.
Minimum Spanning Tree
Prim’s Algorithm
Idea
Prim’s algorithm finds a Minimum Spanning Tree (MST) for a weighted undirected graph by starting from an arbitrary vertex and continuously adding the smallest edge that connects a vertex in the MST to a vertex outside it.
Given an array points where points[i] = [xi, yi] represents a point on the X-Y plane, return the minimum cost to make all points connected. The cost between two points is the Manhattan distance.
Solution:
We can use Prim’s algorithm to find the MST connecting all points.
Cycle Detection
Idea
Detecting cycles in graphs is essential for avoiding infinite loops and deadlocks. In an undirected graph, cycles can be detected using DFS by keeping track of visited nodes and their parents.
There are n courses labeled from 0 to n-1. Some courses have prerequisites. Determine if it’s possible to finish all courses. If there is a cycle, it’s impossible.
Solution:
We can detect cycles in the course prerequisite graph using DFS.