Given an m x n integers matrix, return the length of the longest increasing path in the matrix.
From each cell, you can either move in four directions: left, right, up, or down. You may not move diagonally or move outside the boundary (i.e., wrap-around is not allowed).
Input: matrix = [[9,9,4],[6,6,8],[2,1,1]]
Output: 4
Explanation: The longest increasing path is [1, 2, 6, 9].
Input: matrix = [[3,4,5],[3,2,6],[2,2,1]]
Output: 4
Explanation: The longest increasing path is [3, 4, 5, 6]. Moving diagonally is not allowed.
Input: matrix = [[1]]
Output: 1
m == matrix.lengthn == matrix[i].length1 <= m, n <= 2000 <= matrix[i][j] <= 2^31 - 1This problem can be solved using DFS with Memoization or Topological Sort.
The key insight is that for each cell, we can compute the longest increasing path starting from that cell. However, doing a naive DFS from every cell would result in O(2^(m*n)) time complexity due to overlapping subproblems.
By using memoization, we can cache the result for each cell and avoid recomputation, reducing the time complexity to O(m * n).
memo[i][j] to store the longest path starting from cell (i, j)dfs(i, j) = 1 + max(dfs(ni, nj)) for all valid neighbors (ni, nj)
where matrix[ni][nj] > matrix[i][j]
Base case: dfs(i, j) = 1 (single cell path)
We can think of the matrix as a directed acyclic graph (DAG) where there's an edge from cell A to cell B if B's value is greater than A's value and B is adjacent to A.
The longest path in a DAG can be found using topological sort:
Time Complexity: O(m × n)
Space Complexity: O(m × n)
Time Complexity: O(m × n)
Space Complexity: O(m × n)
1class Solution {
2 // Approach 1: DFS with Memoization
3 // Time: O(m * n), Space: O(m * n)
4 public int longestIncreasingPath(int[][] matrix) {
5 if (matrix == null || matrix.length == 0) {
6 return 0;
7 }
8
9 int m = matrix.length;
10 int n = matrix[0].length;
11 int[][] memo = new int[m][n];
12 int maxPath = 0;
13
14 for (int i = 0; i < m; i++) {
15 for (int j = 0; j < n; j++) {
16 maxPath = Math.max(maxPath, dfs(matrix, i, j, memo));
17 }
18 }
19
20 return maxPath;
21 }
22
23 private int dfs(int[][] matrix, int row, int col, int[][] memo) {
24 // Return memoized result if available
25 if (memo[row][col] != 0) {
26 return memo[row][col];
27 }
28
29 int m = matrix.length;
30 int n = matrix[0].length;
31 int maxLength = 1; // Base case: single cell path
32
33 // Explore all four directions
34 int[][] directions = {{0, 1}, {1, 0}, {0, -1}, {-1, 0}};
35
36 for (int[] dir : directions) {
37 int newRow = row + dir[0];
38 int newCol = col + dir[1];
39
40 // Check bounds and increasing condition
41 if (newRow >= 0 && newRow < m &&
42 newCol >= 0 && newCol < n &&
43 matrix[newRow][newCol] > matrix[row][col]) {
44
45 int length = 1 + dfs(matrix, newRow, newCol, memo);
46 maxLength = Math.max(maxLength, length);
47 }
48 }
49
50 // Memoize and return result
51 memo[row][col] = maxLength;
52 return maxLength;
53 }
54
55 // Approach 2: Topological Sort (BFS)
56 // Time: O(m * n), Space: O(m * n)
57 public int longestIncreasingPathTopological(int[][] matrix) {
58 if (matrix == null || matrix.length == 0) {
59 return 0;
60 }
61
62 int m = matrix.length;
63 int n = matrix[0].length;
64 int[][] outdegree = new int[m][n];
65 int[][] directions = {{0, 1}, {1, 0}, {0, -1}, {-1, 0}};
66
67 // Calculate outdegree for each cell
68 for (int i = 0; i < m; i++) {
69 for (int j = 0; j < n; j++) {
70 for (int[] dir : directions) {
71 int ni = i + dir[0];
72 int nj = j + dir[1];
73 if (ni >= 0 && ni < m && nj >= 0 && nj < n &&
74 matrix[ni][nj] > matrix[i][j]) {
75 outdegree[i][j]++;
76 }
77 }
78 }
79 }
80
81 // Start BFS from leaves (outdegree == 0)
82 java.util.Queue<int[]> queue = new java.util.LinkedList<>();
83 for (int i = 0; i < m; i++) {
84 for (int j = 0; j < n; j++) {
85 if (outdegree[i][j] == 0) {
86 queue.offer(new int[]{i, j});
87 }
88 }
89 }
90
91 int length = 0;
92 while (!queue.isEmpty()) {
93 int size = queue.size();
94 length++;
95 for (int k = 0; k < size; k++) {
96 int[] cell = queue.poll();
97 int i = cell[0];
98 int j = cell[1];
99
100 for (int[] dir : directions) {
101 int ni = i + dir[0];
102 int nj = j + dir[1];
103 if (ni >= 0 && ni < m && nj >= 0 && nj < n &&
104 matrix[ni][nj] < matrix[i][j]) {
105 outdegree[ni][nj]--;
106 if (outdegree[ni][nj] == 0) {
107 queue.offer(new int[]{ni, nj});
108 }
109 }
110 }
111 }
112 }
113
114 return length;
115 }
116}
117