🧠
Topological Sort Based DP (Bottom-Up)
💡 This approach treats the matrix as a DAG where edges go from smaller to larger values, then uses topological sorting to compute longest paths iteratively.
Intuition
Sort cells by their values ascendingly, then for each cell, update neighbors with larger values by relaxing their dp values, building longest paths bottom-up.
Algorithm
- Create a list of all cells and sort them by their values ascendingly.
- Initialize a dp matrix with 1s since minimum path length is 1.
- Iterate over cells in sorted order, for each cell update dp of neighbors with larger values.
- Return the maximum dp value found.
💡 This approach uses the DAG property of the matrix values to compute dp iteratively without recursion.
Recurrence:dp[i][j] = max(dp[i][j], 1 + dp[x][y]) for neighbors (x,y) with matrix[x][y] < matrix[i][j]
from typing import List
class Solution:
def longestIncreasingPath(self, matrix: List[List[int]]) -> int:
if not matrix or not matrix[0]:
return 0
m, n = len(matrix), len(matrix[0])
cells = [(i, j) for i in range(m) for j in range(n)]
cells.sort(key=lambda x: matrix[x[0]][x[1]])
dp = [[1]*n for _ in range(m)]
directions = [(-1,0),(1,0),(0,-1),(0,1)]
for i, j in cells:
for dx, dy in directions:
x, y = i + dx, j + dy
if 0 <= x < m and 0 <= y < n and matrix[x][y] > matrix[i][j]:
if dp[x][y] < dp[i][j] + 1:
dp[x][y] = dp[i][j] + 1
return max(max(row) for row in dp)
# Example usage:
# sol = Solution()
# print(sol.longestIncreasingPath([[9,9,4],[6,6,8],[2,1,1]])) # Output: 4
Line Notes
cells = [(i, j) for i in range(m) for j in range(n)]Create list of all cell coordinates
cells.sort(key=lambda x: matrix[x[0]][x[1]])Sort cells by their values ascendingly
dp = [[1]*n for _ in range(m)]Initialize dp with 1 since each cell is a path of length 1
for i, j in cells:Process cells in ascending order of values
if dp[x][y] < dp[i][j] + 1:Relax dp value for neighbor with larger value
return max(max(row) for row in dp)Return maximum path length found
import java.util.*;
public class Solution {
private int m, n;
private int[][] matrix, dp;
public int longestIncreasingPath(int[][] matrix) {
if (matrix == null || matrix.length == 0 || matrix[0].length == 0) return 0;
this.matrix = matrix;
m = matrix.length;
n = matrix[0].length;
dp = new int[m][n];
List<int[]> cells = new ArrayList<>();
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
cells.add(new int[]{i, j});
dp[i][j] = 1;
}
}
cells.sort(Comparator.comparingInt(a -> matrix[a[0]][a[1]]));
int[][] dirs = {{-1,0},{1,0},{0,-1},{0,1}};
for (int[] cell : cells) {
int i = cell[0], j = cell[1];
for (int[] d : dirs) {
int x = i + d[0], y = j + d[1];
if (x >= 0 && x < m && y >= 0 && y < n && matrix[x][y] > matrix[i][j]) {
if (dp[x][y] < dp[i][j] + 1) {
dp[x][y] = dp[i][j] + 1;
}
}
}
}
int result = 0;
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
if (dp[i][j] > result) result = dp[i][j];
}
}
return result;
}
// Example usage:
// public static void main(String[] args) {
// Solution sol = new Solution();
// int[][] matrix = {{9,9,4},{6,6,8},{2,1,1}};
// System.out.println(sol.longestIncreasingPath(matrix)); // Output: 4
// }
}
Line Notes
List<int[]> cells = new ArrayList<>();Store all cell coordinates
cells.sort(Comparator.comparingInt(a -> matrix[a[0]][a[1]]));Sort cells by their values ascendingly
dp[i][j] = 1;Initialize dp with 1 for each cell
for (int[] cell : cells)Process cells in ascending order
if (dp[x][y] < dp[i][j] + 1)Relax dp value for neighbor with larger value
return result;Return maximum path length found
for (int i = 0; i < m; i++)Iterate rows to find max dp
for (int j = 0; j < n; j++)Iterate columns to find max dp
#include <vector>
#include <algorithm>
#include <iostream>
using namespace std;
class Solution {
int m, n;
vector<vector<int>> matrix, dp;
public:
int longestIncreasingPath(vector<vector<int>>& mat) {
if (mat.empty() || mat[0].empty()) return 0;
matrix = mat;
m = matrix.size();
n = matrix[0].size();
dp.assign(m, vector<int>(n, 1));
vector<pair<int,int>> cells;
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
cells.emplace_back(i, j);
}
}
sort(cells.begin(), cells.end(), [&](auto &a, auto &b) {
return matrix[a.first][a.second] < matrix[b.first][b.second];
});
int dirs[4][2] = {{-1,0},{1,0},{0,-1},{0,1}};
for (auto& cell : cells) {
int i = cell.first, j = cell.second;
for (auto& d : dirs) {
int x = i + d[0], y = j + d[1];
if (x >= 0 && x < m && y >= 0 && y < n && matrix[x][y] > matrix[i][j]) {
if (dp[x][y] < dp[i][j] + 1) {
dp[x][y] = dp[i][j] + 1;
}
}
}
}
int result = 0;
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
if (dp[i][j] > result) result = dp[i][j];
}
}
return result;
}
};
// Example usage:
// int main() {
// Solution sol;
// vector<vector<int>> matrix = {{9,9,4},{6,6,8},{2,1,1}};
// cout << sol.longestIncreasingPath(matrix) << endl; // Output: 4
// return 0;
// }
Line Notes
dp.assign(m, vector<int>(n, 1));Initialize dp with 1 for each cell
cells.emplace_back(i, j);Store all cell coordinates
sort(cells.begin(), cells.end(), ...Sort cells by their values ascendingly
if (dp[x][y] < dp[i][j] + 1)Relax dp value for neighbor with larger value
for (auto& cell : cells)Process cells in ascending order
return result;Return maximum path length found
for (int i = 0; i < m; i++)Iterate rows to find max dp
for (int j = 0; j < n; j++)Iterate columns to find max dp
var longestIncreasingPath = function(matrix) {
if (!matrix || matrix.length === 0 || matrix[0].length === 0) return 0;
const m = matrix.length, n = matrix[0].length;
const dp = Array.from({length: m}, () => Array(n).fill(1));
const cells = [];
for (let i = 0; i < m; i++) {
for (let j = 0; j < n; j++) {
cells.push([i, j]);
}
}
cells.sort((a, b) => matrix[a[0]][a[1]] - matrix[b[0]][b[1]]);
const directions = [[-1,0],[1,0],[0,-1],[0,1]];
for (const [i, j] of cells) {
for (const [dx, dy] of directions) {
const x = i + dx, y = j + dy;
if (x >= 0 && x < m && y >= 0 && y < n && matrix[x][y] > matrix[i][j]) {
if (dp[x][y] < dp[i][j] + 1) {
dp[x][y] = dp[i][j] + 1;
}
}
}
}
let result = 0;
for (let i = 0; i < m; i++) {
for (let j = 0; j < n; j++) {
if (dp[i][j] > result) result = dp[i][j];
}
}
return result;
};
// Example usage:
// console.log(longestIncreasingPath([[9,9,4],[6,6,8],[2,1,1]])); // Output: 4
Line Notes
const dp = Array.from({length: m}, () => Array(n).fill(1));Initialize dp with 1 for each cell
cells.push([i, j]);Store all cell coordinates
cells.sort((a, b) => matrix[a[0]][a[1]] - matrix[b[0]][b[1]]);Sort cells by their values ascendingly
if (dp[x][y] < dp[i][j] + 1)Relax dp value for neighbor with larger value
for (const [i, j] of cells)Process cells in ascending order
return result;Return maximum path length found
for (let i = 0; i < m; i++)Iterate rows to find max dp
for (let j = 0; j < n; j++)Iterate columns to find max dp
TimeO(m * n * log(m * n))
SpaceO(m * n)
Sorting all cells takes O(m*n*log(m*n)) and each cell updates up to 4 neighbors.
💡 For a 200x200 matrix, sorting 40,000 cells is efficient and the DP updates are linear.
Interview Verdict: Accepted
This approach is an elegant bottom-up alternative to memoized DFS and is acceptable in interviews.