DEV Community

swati goyal
swati goyal

Posted on

🚀 LeetCode 329: Longest Increasing Path in a Matrix (DFS + Memoization) | Easy Explanation

One of the most common mistakes while solving graph problems on matrices is accidentally turning an O(m*n) problem into exponential recursion 😅

This problem is a perfect example of how DFS + Memoization can optimize brute force beautifully.


📌 Problem Statement

Given an m x n matrix, find the length of the longest increasing path.

You can move only in 4 directions:

  • ⬆️ Up
  • ⬇️ Down
  • ⬅️ Left
  • ➡️ Right

Diagonal movement is NOT allowed.


🧠 Example

Input:
[
 [9,9,4],
 [6,6,8],
 [2,1,1]
]

Output: 4
Enter fullscreen mode Exit fullscreen mode

Longest increasing path:

1 → 2 → 6 → 9
Enter fullscreen mode Exit fullscreen mode

❌ Brute Force Idea

For every cell:

  • Start DFS
  • Try all 4 directions
  • Find longest increasing path

Problem?

We repeatedly calculate the same paths again and again.

This causes:

Exponential Time Complexity 😭
Enter fullscreen mode Exit fullscreen mode

✅ Optimization: Memoization (DP)

Instead of recalculating:

Store the answer for each cell.

dp[i][j] = longest increasing path starting from (i, j)
Enter fullscreen mode Exit fullscreen mode

So once computed, we directly reuse it.


🔥 Key Observation

If we already know:

Longest path from (x2, y2) = 5
Enter fullscreen mode Exit fullscreen mode

Then:

Longest path from (x, y) = 1 + 5
Enter fullscreen mode Exit fullscreen mode

No need to compute again.


📌 DFS Flow

For every cell:

  1. Explore all 4 directions
  2. Move only if next cell is greater
  3. Take maximum path among all valid moves
  4. Store result in DP

✅ Java Solution

class Solution {

    int[] dx = {-1, 0, 1, 0};
    int[] dy = {0, 1, 0, -1};

    public int longestIncreasingPath(int[][] matrix) {
        int m = matrix.length;
        int n = matrix[0].length;
        int[][] dp = new int[m][n];

        int res = 0;
        for(int i = 0; i < m; i++) {
            for(int j = 0; j < n; j++) {
                res = Math.max(res, dfs(matrix, i, j, dp));
            }
        }

        return res;
    }

    public int dfs(int[][] matrix, int x, int y, int[][] dp) {
        if(dp[x][y] != 0) {
            return dp[x][y];
        }

        dp[x][y] = 1;

        for(int i = 0; i < 4; i++) {
            int x2 = x + dx[i];
            int y2 = y + dy[i];

            if(isValid(matrix, x, y, x2, y2)) {
                dp[x][y] = Math.max(dp[x][y], 1 + dfs(matrix, x2, y2, dp)
                );
            }
        }

        return dp[x][y];
    }

    public boolean isValid(int[][] matrix, int x, int y, int x2, int y2) {
        int m = matrix.length;
        int n = matrix[0].length;

        if(x2 < 0 || y2 < 0 || x2 >= m || y2 >= n) {
            return false;
        }

        return matrix[x][y] < matrix[x2][y2];
    }
}
Enter fullscreen mode Exit fullscreen mode

🎯 Dry Run

Suppose we start from:

matrix[2][1] = 1
Enter fullscreen mode Exit fullscreen mode

Possible increasing moves:

1 → 2 → 6 → 9
Enter fullscreen mode Exit fullscreen mode

DFS computes:

dp[2][1] = 4
Enter fullscreen mode Exit fullscreen mode

Next time if we visit this cell again:

Directly return 4 from DP ✅
Enter fullscreen mode Exit fullscreen mode

No recomputation.


⏱️ Time Complexity

Without Memoization

Exponential
Enter fullscreen mode Exit fullscreen mode

because we repeatedly explore same paths.


With Memoization

Each cell is computed only once.

Time Complexity: O(m * n)
Enter fullscreen mode Exit fullscreen mode

Why?

  • Total cells = m * n
  • Each cell explores at most 4 directions

So overall:

O(4 * m * n) = O(m * n)
Enter fullscreen mode Exit fullscreen mode

💾 Space Complexity

DP Array

O(m * n)
Enter fullscreen mode Exit fullscreen mode

Recursion Stack

Worst case increasing path:

O(m * n)
Enter fullscreen mode Exit fullscreen mode

Final Space Complexity

O(m * n)
Enter fullscreen mode Exit fullscreen mode

🧭 Understanding the Direction Arrays

int[] dx = {-1, 0, 1, 0};
int[] dy = {0, 1, 0, -1};
Enter fullscreen mode Exit fullscreen mode

These arrays help us move in all 4 valid directions inside the matrix.

Think of it like this:

Direction dx dy
Up ⬆️ -1 0
Right ➡️ 0 1
Down ⬇️ 1 0
Left ⬅️ 0 -1

Suppose current cell is:

(x, y)
Enter fullscreen mode Exit fullscreen mode

Then:

x2 = x + dx[i]
y2 = y + dy[i]
Enter fullscreen mode Exit fullscreen mode

generates the next position.


🔍 Example

Current cell:

(2, 3)
Enter fullscreen mode Exit fullscreen mode

1️⃣ Up

dx = -1
dy = 0
Enter fullscreen mode Exit fullscreen mode

New position:

(2 - 1, 3 + 0)
= (1, 3)
Enter fullscreen mode Exit fullscreen mode

Move upward ⬆️


2️⃣ Right

dx = 0
dy = 1
Enter fullscreen mode Exit fullscreen mode

New position:

(2, 4)
Enter fullscreen mode Exit fullscreen mode

Move right ➡️


3️⃣ Down

dx = 1
dy = 0
Enter fullscreen mode Exit fullscreen mode

New position:

(3, 3)
Enter fullscreen mode Exit fullscreen mode

Move downward ⬇️


4️⃣ Left

dx = 0
dy = -1
Enter fullscreen mode Exit fullscreen mode

New position:

(2, 2)
Enter fullscreen mode Exit fullscreen mode

Move left ⬅️


This is a very common matrix traversal pattern used in:

  • DFS
  • BFS
  • Flood Fill
  • Islands problems
  • Graph traversal on grids
  • Shortest path problems

🔥 Dry Run

Matrix:

[
 [9, 9, 4],
 [6, 6, 8],
 [2, 1, 1]
]
Enter fullscreen mode Exit fullscreen mode

We start DFS from:

matrix[2][1] = 1
Enter fullscreen mode Exit fullscreen mode

because smaller numbers usually create longer increasing paths.


Step 1️⃣

Current cell:

(2,1) = 1
Enter fullscreen mode Exit fullscreen mode

Explore all 4 directions.


Up ⬆️

(1,1) = 6
Enter fullscreen mode Exit fullscreen mode

Valid because:

6 > 1
Enter fullscreen mode Exit fullscreen mode

So:

1 → 6
Enter fullscreen mode Exit fullscreen mode

Now recursively call DFS on (1,1).


Step 2️⃣

Current cell:

(1,1) = 6
Enter fullscreen mode Exit fullscreen mode

Explore directions.


Up ⬆️

(0,1) = 9
Enter fullscreen mode Exit fullscreen mode

Valid because:

9 > 6
Enter fullscreen mode Exit fullscreen mode

Path becomes:

1 → 6 → 9
Enter fullscreen mode Exit fullscreen mode

DFS on (0,1).


Step 3️⃣

Current cell:

(0,1) = 9
Enter fullscreen mode Exit fullscreen mode

No adjacent greater value exists.

So:

dp[0][1] = 1
Enter fullscreen mode Exit fullscreen mode

Return back.


Step 4️⃣

Back to:

(1,1) = 6
Enter fullscreen mode Exit fullscreen mode

We now know:

Longest path from 9 = 1
Enter fullscreen mode Exit fullscreen mode

So:

dp[1][1] = 1 + 1 = 2
Enter fullscreen mode Exit fullscreen mode

Return back.


Step 5️⃣

Back to:

(2,1) = 1
Enter fullscreen mode Exit fullscreen mode

Current best:

1 → 6 → 9
Length = 3
Enter fullscreen mode Exit fullscreen mode

But continue exploring other directions.


Left ⬅️

(2,0) = 2
Enter fullscreen mode Exit fullscreen mode

Valid because:

2 > 1
Enter fullscreen mode Exit fullscreen mode

Path:

1 → 2
Enter fullscreen mode Exit fullscreen mode

DFS on (2,0).


Step 6️⃣

Current cell:

(2,0) = 2
Enter fullscreen mode Exit fullscreen mode

Up:

(1,0) = 6
Enter fullscreen mode Exit fullscreen mode

Valid.

Then from 6:

6 → 9
Enter fullscreen mode Exit fullscreen mode

Final path:

1 → 2 → 6 → 9
Enter fullscreen mode Exit fullscreen mode

Length:

4
Enter fullscreen mode Exit fullscreen mode

So:

dp[2][1] = 4
Enter fullscreen mode Exit fullscreen mode

💡 Most Important Optimization

Now suppose another DFS again reaches:

(1,0) = 6
Enter fullscreen mode Exit fullscreen mode

We already computed:

dp[1][0] = 2
Enter fullscreen mode Exit fullscreen mode

So we directly reuse it instead of recalculating.

That is exactly why memoization makes this solution:

O(m * n)
Enter fullscreen mode Exit fullscreen mode

instead of exponential 🚀


🔥 Important Interview Insights

This problem teaches:

✅ DFS on Matrix
✅ Memoization
✅ DP on Graphs
✅ Avoiding repeated recursion
✅ Converting exponential → linear complexity


⚠️ Common Mistakes

1. Using diagonal directions

Wrong:

{-1,-1}, {-1,1}, {1,-1}, {1,1}
Enter fullscreen mode Exit fullscreen mode

Only 4 directions allowed.


2. Forgetting memoization

Without DP → TLE.


3. Boundary check mistake

Always validate:

x2 and y2
Enter fullscreen mode Exit fullscreen mode

NOT current coordinates.


🚀 Final Thoughts

At first glance, this problem feels like a hard graph DFS problem.

But once you identify:

Overlapping Subproblems
Enter fullscreen mode Exit fullscreen mode

it becomes a very elegant DFS + Memoization problem.

One of my favorite patterns for matrix DFS problems 🔥


💬 What do you think?

  • Did you solve it using DFS or Topological Sort?
  • Have you seen similar memoization patterns in interviews?
  • Which matrix problem should I break down next?

Would love to discuss 👇

Top comments (0)