Longest Increasing Path in a Matrix
Hard · 2-D Dynamic Programming · Explanation and code example
Topological Sort (Kahn's Algorithm)
How it works
We can think of the matrix as a directed graph:
- each cell is a node
- we draw a directed edge from a smaller value to a larger value (only for 4-direction neighbors)
Because edges always go from smaller to larger, the graph has no cycles (values must strictly increase), so it is a DAG.
In a DAG, the longest path length can be found using topological order.
Kahn’s algorithm (BFS with indegrees) processes nodes level by level:
- nodes with indegree
0are the “smallest” starting points (no smaller neighbor points into them) - removing one layer may unlock the next larger layer
Each BFS layer corresponds to taking one step forward in an increasing path.
So, the number of layers processed is exactly the length of the longest increasing path.
Steps
- Treat each cell
(r, c)as a node. - Compute
indegree[r][c]:indegree[r][c]= number of neighboring cells with a smaller value (neighbors that point into(r, c))- For each cell, look at its 4 neighbors:
- if a neighbor value is smaller, increase the cell's indegree
- Initialize a queue with all cells that have indegree
0:- these are local minima and can start an increasing path
- Perform BFS in layers:
- for each layer:
- pop all current nodes in the queue
- for each popped cell, check its 4 neighbors
- if a neighbor has a larger value, it is reachable from the current cell:
- decrement that neighbor's
indegree - if the neighbor's
indegreebecomes0, push it into the queue
- decrement that neighbor's
- after finishing the layer, increment the answer (
LIS += 1)
- for each layer:
- When the queue becomes empty, all nodes have been processed.
- Return the number of layers processed (
LIS), which equals the longest increasing path length.
Complexity
- Time complexity:
O(m * n) - Space complexity:
O(m * n)
Where
mis the number of rows andnis the number of columns in the givenmatrix.
Things to watch for
Using a Visited Array Instead of Value Comparison
Unlike typical graph traversal problems, this problem does not require a visited array. The strictly increasing constraint naturally prevents revisiting cells since you cannot go back to a smaller value. Using a visited array unnecessarily complicates the solution and can cause bugs when the same cell should be explored from different starting points.
Forgetting to Try All Starting Cells
The longest increasing path might not start from any corner or edge cell. You must try starting the DFS from every cell in the matrix and track the global maximum. A common mistake is assuming the path starts from the minimum or maximum value cell.
Incorrect Memoization Key
When memoizing results, the cache key should be just the cell coordinates (r, c), not including the previous value. The longest path starting from a cell is independent of how you reached that cell because you can only move to strictly larger values. Including prevVal in the cache key wastes memory and misses cache hits.
Using Non-Strict Inequality
The problem requires strictly increasing values. Using >= instead of > when comparing neighboring cells allows equal values to be included in the path, which violates the problem constraints and leads to incorrect answers or infinite loops.
Not Handling Edge Cases for Small Matrices
For a 1x1 matrix, the answer is always 1. Some solutions fail to handle this case properly, especially when the logic assumes there will always be valid neighbors to explore.
Python code example
from typing import List
from collections import deque
class Solution:
def longestIncreasingPath(self, matrix: List[List[int]]) -> int:
ROWS, COLS = len(matrix), len(matrix[0])
directions = [[-1, 0], [1, 0], [0, -1], [0, 1]]
indegree = [[0] * COLS for _ in range(ROWS)]
for r in range(ROWS):
for c in range(COLS):
for d in directions:
nr, nc = d[0] + r, d[1] + c
if (0 <= nr < ROWS and 0 <= nc < COLS and
matrix[nr][nc] < matrix[r][c]
):
indegree[r][c] += 1
q = deque()
for r in range(ROWS):
for c in range(COLS):
if indegree[r][c] == 0:
q.append([r, c])
LIS = 0
while q:
for _ in range(len(q)):
r, c = q.popleft()
for d in directions:
nr, nc = r + d[0], c + d[1]
if (0 <= nr < ROWS and 0 <= nc < COLS and
matrix[nr][nc] > matrix[r][c]
):
indegree[nr][nc] -= 1
if indegree[nr][nc] == 0:
q.append([nr, nc])
LIS += 1
return LIS
C++ code example
class Solution {
public:
int longestIncreasingPath(vector<vector<int>>& matrix) {
int ROWS = matrix.size(), COLS = matrix[0].size();
vector<vector<int>> indegree(ROWS, vector<int>(COLS, 0));
vector<vector<int>> directions = {{-1, 0}, {1, 0},
{0, -1}, {0, 1}};
for (int r = 0; r < ROWS; ++r) {
for (int c = 0; c < COLS; ++c) {
for (auto& d : directions) {
int nr = r + d[0], nc = c + d[1];
if (nr >= 0 && nr < ROWS && nc >= 0 &&
nc < COLS && matrix[nr][nc] < matrix[r][c]) {
indegree[r][c]++;
}
}
}
}
queue<pair<int, int>> q;
for (int r = 0; r < ROWS; ++r) {
for (int c = 0; c < COLS; ++c) {
if (indegree[r][c] == 0) {
q.push({r, c});
}
}
}
int LIS = 0;
while (!q.empty()) {
int size = q.size();
for (int i = 0; i < size; ++i) {
auto [r, c] = q.front();
q.pop();
for (auto& d : directions) {
int nr = r + d[0], nc = c + d[1];
if (nr >= 0 && nr < ROWS && nc >= 0 &&
nc < COLS && matrix[nr][nc] > matrix[r][c]) {
if (--indegree[nr][nc] == 0) {
q.push({nr, nc});
}
}
}
}
LIS++;
}
return LIS;
}
};
Source and license
Adapted from NeetCode’s source · MIT license. Node types are supplied by LeetCode where needed.