Given an array of integers nums, find the subarray with the largest sum and return the sum.
A subarray is a contiguous non-empty sequence of elements within an array.
Example 1:
Input: nums = [2,-3,4,-2,2,1,-1,4]
Output: 8Explanation: The subarray [4,-2,2,1,-1,4] has the largest sum 8.
Example 2:
Input: nums = [-1]
Output: -1Constraints:
1 <= nums.length <= 1000-1000 <= nums[i] <= 1000
You should aim for a solution with O(n) time and O(1) space, where n is the size of the input array.
A brute force approach would be to compute the sum of every subarray and return the maximum among them. This would be an O(n^2) approach. Can you think of a better way? Maybe you should consider a dynamic programming-based approach.
Instead of calculating the sum for every subarray, try maintaining a running sum. Maybe you should consider whether extending the previous sum or starting fresh with the current element gives a better result. Can you think of a way to track this efficiently?
We use a variable curSum to track the sum of the elements. At each index, we have two choices: either add the current element to curSum or start a new subarray by resetting curSum to the current element. Maybe you should track the maximum sum at each step and update the global maximum accordingly.
This algorithm is known as Kadane's algorithm.
This problem asks us to find the maximum sum of any contiguous subarray.
The most straightforward way to think about this is:
A subarray is defined by a start index i and an end index j.
By fixing i and expanding j to the right, we can compute the sum of all subarrays that start at i.
This approach is easy to understand and works well for learning, but it is not efficient for large inputs.
n be the length of the array.res with the first element of the array.i from 0 to n - 1:i:cur = 0j from i to n - 1:nums[j] to curres with the maximum of res and curres.We want the maximum sum of a contiguous subarray.
Using recursion, we can think of the problem as making a decision at each index:
The recursive function keeps track of this using a flag:
flag = False - we have not started a subarray yetflag = True - we are currently building a subarrayThe function answers the question:
“What is the maximum subarray sum we can get starting from index i, given whether we are already inside a subarray or not?”
By exploring both possibilities at every step, the recursion eventually finds the best contiguous subarray.
dfs(i, flag):i is the current index in the arrayflag indicates whether a subarray has already startedi reaches the end of the array:0 if a subarray was already startedflag is True (we are inside a subarray):0nums[i] and recurseflag is False (we have not started yet):dfs(0, False)We want to find the maximum sum of a contiguous subarray.
In the recursive solution, we modeled the problem using two states:
However, plain recursion repeats the same computations many times.
To optimize this, we use top-down dynamic programming (memoization).
Each state is uniquely identified by:
i: the current index in the arrayflag: whether a subarray has already started (True) or not (False).The function answers:
“What is the maximum subarray sum we can get starting from index i, given whether a subarray is already in progress?”
By storing results for each (i, flag) state, we avoid recomputing them.
memo where:memo[i][flag] stores the maximum subarray sum starting at index idfs(i, flag):i is the current indexflag indicates whether a subarray is already in progress.i reaches the end of the array:0 if a subarray was started(i, flag) is already stored in memo:flag is True (inside a subarray):0)nums[i]flag is False (not started yet):dfs(0, False).class Solution:
def maxSubArray(self, nums: List[int]) -> int:
memo = [[None] * 2 for _ in range(len(nums) + 1)]
def dfs(i, flag):
if i == len(nums):
return 0 if flag else -1e6
if memo[i][flag] is not None:
return memo[i][flag]
if flag:
memo[i][flag] = max(0, nums[i] + dfs(i + 1, True))
else:
memo[i][flag] = max(dfs(i + 1, False),
nums[i] + dfs(i + 1, True))
return memo[i][flag]
return dfs(0, False)We want the maximum sum of a contiguous subarray.
From the recursive and top-down DP solutions, we observed two useful states:
iiInstead of recursion, we can compute these values iteratively using bottom-up dynamic programming.
At each index, we decide:
By filling the DP table from right to left, all needed future values are already known.
n be the length of the array.dp of size n x 2:dp[i][1] = maximum subarray sum that must start at index idp[i][0] = maximum subarray sum that starts at index i or laterdp[n - 1][1] = nums[n - 1]dp[n - 1][0] = nums[n - 1]i from n - 2 down to 0:dp[i][1]:i - nums[i]i + 1 - nums[i] + dp[i + 1][1]dp[i][0]:dp[i + 1][0]i - dp[i][1]dp[0][0] contains the maximum subarray sum.dp[0][0].class Solution:
def maxSubArray(self, nums: List[int]) -> int:
n = len(nums)
dp = [[0] * 2 for _ in range(n)]
dp[n - 1][1] = dp[n - 1][0] = nums[n - 1]
for i in range(n - 2, -1, -1):
dp[i][1] = max(nums[i], nums[i] + dp[i + 1][1])
dp[i][0] = max(dp[i + 1][0], dp[i][1])
return dp[0][0]We want the maximum sum of a contiguous subarray.
At every position, we have a simple choice:
If the sum up to the previous index is negative, extending it would only make things worse, so we start fresh at the current element.
This idea allows us to keep track of the best subarray sum ending at each index and update it in a single pass.
dp where:dp[i] represents the maximum subarray sum ending at index i.dp as a copy of nums since the smallest subarray ending at each index is the element itself.1 to the end:dp[i] as:nums[i]nums[i] + dp[i - 1].dp.We want the maximum sum of a contiguous subarray.
Kadane’s Algorithm is based on one simple observation:
So whenever the current sum drops below zero, we reset it and start a new subarray from the next element.
As we scan the array once, we keep track of:
curSum = 0 to track the running subarray summaxSub as the first element (handles all-negative arrays).curSum becomes negative:0 (start a new subarray).curSum.maxSub with the maximum of maxSub and curSum.maxSub.We want the maximum sum of a contiguous subarray.
Using divide and conquer, we split the array into two halves and solve the problem recursively.
For any subarray [l .. r], the maximum subarray must be one of these three cases:
The first two cases are solved recursively.
The third case is handled by:
The recursive function represents:
“What is the maximum subarray sum within the range [l .. r]?”
dfs(l, r):l > r, return negative infinity (invalid range).m of the range [l .. r].m - 1 to l, keeping the maximum prefix summ + 1 to r, keeping the maximum prefix sumnums[m].dfs(l, m - 1)dfs(m + 1, r).[0 .. n - 1].class Solution:
def maxSubArray(self, nums: List[int]) -> int:
def dfs(l, r):
if l > r:
return float("-inf")
m = (l + r) >> 1
leftSum = rightSum = curSum = 0
for i in range(m - 1, l - 1, -1):
curSum += nums[i]
leftSum = max(leftSum, curSum)
curSum = 0
for i in range(m + 1, r + 1):
curSum += nums[i]
rightSum = max(rightSum, curSum)
return (max(dfs(l, m - 1),
dfs(m + 1, r),
leftSum + nums[m] + rightSum))
return dfs(0, len(nums) - 1)When all elements in the array are negative, the maximum subarray sum is the largest negative number, not zero. Initializing maxSum to 0 instead of nums[0] (or negative infinity) causes the algorithm to incorrectly return 0 for all-negative arrays. Always initialize with the first element or a sufficiently small value.
In Kadane's algorithm, you should reset curSum to zero when it becomes negative, not when it becomes less than the current element. The condition if (curSum < 0) curSum = 0 should come before adding the current element, not after. Placing this check incorrectly changes when subarrays restart and produces wrong results.