Jump Game

Finding the Maximum Subarray Sum

The Maximum Subarray Problem is a classic algorithmic challenge that frequently appears in coding interviews and real-world applications. The goal is to identify the contiguous subarray within a given array of integers that has the largest sum.

This article provides a clear and efficient approach to solve the problem using Kadane’s Algorithm.

Maximum Subarray

Problem Description

You are given an integer array, nums, where each element nums[i] represents the maximum jump length you can take from that index. Starting from index 0, your goal is to determine whether it is possible to reach the last index.

Explanation:
The subarray [4,-2,2,1,-1,4] has the largest sum 8.

Constraints:

  • 1 <= nums.length <= 1000
  • 0 <= nums[i] <= 1000

Solution Approach: Greedy Algorithm

The key to solving this problem lies in determining the farthest index you can reach at any given point. Using a greedy algorithm,

we can iteratively update the farthest reachable index and check if it eventually encompasses the last index.

Steps

  1. Initialize the Farthest Reachable Index:
    Start with farthest = 0.

  2. Iterate Through the Array:
    For each index i:

    • If i is greater than farthest, it means you cannot progress further, so return false.
    • Update farthest to the maximum of its current value and i + nums[i].
  3. Check Final Reach:
    If you finish the loop and farthest is greater than or equal to the last index, return true.

There are mainly Four  approach to solve this problem – 

  1. Recursion
  2. Dynamic Programming (Top-Down)
  3. Dynamic Programming (Bottom-Up)
  4. Greedy

1. Recursion

  • Time complexity: O(n!)
  • Space complexity: O(n)
class Solution:
    def canJump(self, nums: List[int]) -> bool:
        def dfs(i):
            if i == len(nums) - 1:
                return True
            end = min(len(nums) - 1, i + nums[i])
            for j in range(i + 1, end + 1):
                if dfs(j):
                    return True
            return False

        return dfs(0)

2. Dynamic Programming (Top-Down)

Time & Space Complexity
  • Time complexity: O(n^2)
  • Space complexity: O(n)

Where m is the length of the array queries and n is the length of the array intervals.

class Solution {
public:
    bool canJump(vector& nums) {
        unordered_map memo;
        return dfs(nums, 0, memo);
    }

private:
    bool dfs(vector& nums, int i, unordered_map& memo) {
        if (memo.count(i)) {
            return memo[i];
        }
        if (i == nums.size() - 1) {
            return true;
        }
        if (nums[i] == 0) {
            return false;
        }
        
        int end = min((int)nums.size(), i + nums[i] + 1);
        for (int j = i + 1; j < end; j++) {
            if (dfs(nums, j, memo)) {
                memo[i] = true;
                return true;
            }
        }
        memo[i] = false;
        return false;
    }
};

3. Dynamic Programming (Buttom-Up)

Time & Space Complexity
  • Time complexity: O(n^2)
  • Space complexity: O(n)
class Solution {
public:
    bool canJump(vector& nums) {
        int n = nums.size();
        vector dp(n, false);
        dp[n - 1] = true;

        for (int i = n - 2; i >= 0; i--) {
            int end = min((int)nums.size(), i + nums[i] + 1);
            for (int j = i + 1; j < end; j++) {
                if (dp[j]) {
                    dp[i] = true;
                    break;
                }
            }
        }
        return dp[0];
    }
};

4. Greedy

Time & Space Complexity
  • Time complexity: O(n^2)
  • Space complexity: O(n)

Where m is the length of the array queries and n is the length of the array intervals.

class Solution {
public:
    bool canJump(vector& nums) {
        int goal = nums.size() - 1;

        for (int i = nums.size() - 2; i >= 0; i--) {
            if (i + nums[i] >= goal) {
                goal = i;
            }
        }

        return goal == 0;
    }
};

More Articles