🧠
Top-Down DP with Memoization
💡 Memoization caches results of recursive calls to avoid recomputation, drastically improving efficiency over brute force.
Intuition
Use recursion but store minimum jumps for each index once computed, so repeated calls return cached results.
Algorithm
- Initialize a memo array to store min jumps for each index.
- Define recursive function that returns memoized result if available.
- From current index, try all reachable next positions recursively.
- Store and return the minimum jumps found plus one.
💡 Memoization turns exponential recursion into polynomial by caching results.
Recurrence:dp[i] = min(dp[j] + 1) for all j > i where i + nums[i] >= j
def jump(nums):
n = len(nums)
memo = [-1] * n
def dfs(pos):
if pos >= n - 1:
return 0
if memo[pos] != -1:
return memo[pos]
min_jumps = float('inf')
furthest_jump = min(pos + nums[pos], n - 1)
for next_pos in range(pos + 1, furthest_jump + 1):
jumps = dfs(next_pos)
if jumps != float('inf'):
min_jumps = min(min_jumps, jumps + 1)
memo[pos] = min_jumps
return min_jumps
return dfs(0)
# Example usage
if __name__ == '__main__':
print(jump([2,3,1,1,4])) # Output: 2
Line Notes
memo = [-1] * nInitialize memo array with -1 indicating uncomputed states
if memo[pos] != -1:Return cached result if already computed
memo[pos] = min_jumpsCache computed minimum jumps for current position
return memo[pos]Return cached or newly computed result
import java.util.Arrays;
public class Solution {
private int[] memo;
public int jump(int[] nums) {
memo = new int[nums.length];
Arrays.fill(memo, -1);
return dfs(nums, 0);
}
private int dfs(int[] nums, int pos) {
if (pos >= nums.length - 1) return 0;
if (memo[pos] != -1) return memo[pos];
int minJumps = Integer.MAX_VALUE;
int furthestJump = Math.min(pos + nums[pos], nums.length - 1);
for (int nextPos = pos + 1; nextPos <= furthestJump; nextPos++) {
int jumps = dfs(nums, nextPos);
if (jumps != Integer.MAX_VALUE) {
minJumps = Math.min(minJumps, jumps + 1);
}
}
memo[pos] = minJumps;
return minJumps;
}
public static void main(String[] args) {
Solution sol = new Solution();
System.out.println(sol.jump(new int[]{2,3,1,1,4})); // Output: 2
}
}
Line Notes
memo = new int[nums.length];Create memo array to cache results
Arrays.fill(memo, -1);Initialize memo with -1 to mark unvisited states
if (memo[pos] != -1) return memo[pos];Return cached result if available
memo[pos] = minJumps;Store computed minimum jumps for position
#include <iostream>
#include <vector>
#include <climits>
using namespace std;
int dfs(const vector<int>& nums, int pos, vector<int>& memo) {
if (pos >= (int)nums.size() - 1) return 0;
if (memo[pos] != -1) return memo[pos];
int minJumps = INT_MAX;
int furthestJump = min(pos + nums[pos], (int)nums.size() - 1);
for (int nextPos = pos + 1; nextPos <= furthestJump; ++nextPos) {
int jumps = dfs(nums, nextPos, memo);
if (jumps != INT_MAX) {
minJumps = min(minJumps, jumps + 1);
}
}
memo[pos] = minJumps;
return minJumps;
}
int jump(vector<int>& nums) {
vector<int> memo(nums.size(), -1);
return dfs(nums, 0, memo);
}
int main() {
vector<int> nums = {2,3,1,1,4};
cout << jump(nums) << endl; // Output: 2
return 0;
}
Line Notes
vector<int> memo(nums.size(), -1);Initialize memo vector with -1 for unvisited states
if (memo[pos] != -1) return memo[pos];Return cached result if computed
memo[pos] = minJumps;Cache minimum jumps for current position
return memo[pos];Return cached or computed result
function jump(nums) {
const n = nums.length;
const memo = new Array(n).fill(-1);
function dfs(pos) {
if (pos >= n - 1) return 0;
if (memo[pos] !== -1) return memo[pos];
let minJumps = Infinity;
const furthestJump = Math.min(pos + nums[pos], n - 1);
for (let nextPos = pos + 1; nextPos <= furthestJump; nextPos++) {
const jumps = dfs(nextPos);
if (jumps !== Infinity) {
minJumps = Math.min(minJumps, jumps + 1);
}
}
memo[pos] = minJumps;
return minJumps;
}
return dfs(0);
}
console.log(jump([2,3,1,1,4])); // Output: 2
Line Notes
const memo = new Array(n).fill(-1);Create memo array initialized to -1 for unvisited states
if (memo[pos] !== -1) return memo[pos];Return cached result if available
memo[pos] = minJumps;Cache computed minimum jumps for position
return memo[pos];Return cached or newly computed result
TimeO(n^2) because each index may check all reachable next indices
SpaceO(n) for memo array and recursion stack
Memoization avoids repeated calls but still may check many jumps per index.
💡 For n=1000, this means up to 1 million operations, which is feasible but not optimal.
Interview Verdict: Accepted but can be slow for large inputs
Memoization improves brute force to polynomial time but still not optimal for very large arrays.