Maximum subarray problem
Algorithm · Core
Step through it: Prefix Sum pattern
Now tap the end number.
Step 1 of 5
Question: what is 1 + 4 + 2? Adding three numbers is easy. But if someone asks 1,000 range questions, you re-add the same numbers again and again.
Step 2 of 5
The fix: walk left to right once and write a running total under each number. Each bottom box means everything from the start up to here. The small sum under it shows how it was built.
Step 3 of 5
Want 1 + 4 + 2? Look at the running total under the 2: it is 10. That is too much, because it counts 3 + 1 + 4 + 2 and includes an extra 3 at the front.
Step 4 of 5
Remove the extra part. The running total just before your range is 3, so 10 − 3 = 7. That is two lookups and one subtraction, however long the range is. This list of running totals is called a prefix sum.
Step 5 of 5
Your turn. Tap a start number, then an end number. Try starting at the first number to see the "nothing before" case.
The maximum subarray problem is the task of finding the contiguous
subarray within a one-dimensional array, a[1...n], of numbers
which has the largest sum, where,
Example
The list usually contains both positive and negative numbers along
with 0. For example, for the array of
values −2, 1, −3, 4, −1, 2, 1, −5, 4 the contiguous subarray
with the largest sum is 4, −1, 2, 1, with sum 6.
Solutions
- Brute Force solution
O(n^2): bfMaximumSubarray.js - Divide and Conquer solution
O(n^2): dcMaximumSubarraySum.js - Dynamic Programming solution
O(n): dpMaximumSubarray.js
Code
/**
* Brute Force solution.
* Complexity: O(n^2)
*
* @param {Number[]} inputArray
* @return {Number[]}
*/
export default function bfMaximumSubarray(inputArray) {
let maxSubarrayStartIndex = 0;
let maxSubarrayLength = 0;
let maxSubarraySum = null;
for (let startIndex = 0; startIndex < inputArray.length; startIndex += 1) {
let subarraySum = 0;
for (let arrLength = 1; arrLength <= (inputArray.length - startIndex); arrLength += 1) {
subarraySum += inputArray[startIndex + (arrLength - 1)];
if (maxSubarraySum === null || subarraySum > maxSubarraySum) {
maxSubarraySum = subarraySum;
maxSubarrayStartIndex = startIndex;
maxSubarrayLength = arrLength;
}
}
}
return inputArray.slice(maxSubarrayStartIndex, maxSubarrayStartIndex + maxSubarrayLength);
}/**
* Divide and Conquer solution.
* Complexity: O(n^2) in case if no memoization applied
*
* @param {Number[]} inputArray
* @return {Number[]}
*/
export default function dcMaximumSubarraySum(inputArray) {
/**
* We are going through the inputArray array and for each element we have two options:
* - to pick
* - not to pick
*
* Also keep in mind, that the maximum sub-array must be contiguous. It means if we picked
* the element, we need to continue picking the next elements or stop counting the max sum.
*
* @param {number} elementIndex - the index of the element we're deciding to pick or not
* @param {boolean} mustPick - to pick or not to pick the element
* @returns {number} - maximum subarray sum that we'll get
*/
function solveRecursively(elementIndex, mustPick) {
if (elementIndex >= inputArray.length) {
return mustPick ? 0 : -Infinity;
}
return Math.max(
// Option #1: Pick the current element, and continue picking next one.
inputArray[elementIndex] + solveRecursively(elementIndex + 1, true),
// Option #2: Don't pick the current element.
mustPick ? 0 : solveRecursively(elementIndex + 1, false),
);
}
return solveRecursively(0, false);
}/**
* Dynamic Programming solution.
* Complexity: O(n)
*
* @param {Number[]} inputArray
* @return {Number[]}
*/
export default function dpMaximumSubarray(inputArray) {
// We iterate through the inputArray once, using a greedy approach to keep track of the maximum
// sum we've seen so far and the current sum.
//
// The currentSum variable gets reset to 0 every time it drops below 0.
//
// The maxSum variable is set to -Infinity so that if all numbers are negative, the highest
// negative number will constitute the maximum subarray.
let maxSum = -Infinity;
let currentSum = 0;
// We need to keep track of the starting and ending indices that contributed to our maxSum
// so that we can return the actual subarray. From the beginning let's assume that whole array
// is contributing to maxSum.
let maxStartIndex = 0;
let maxEndIndex = inputArray.length - 1;
let currentStartIndex = 0;
inputArray.forEach((currentNumber, currentIndex) => {
currentSum += currentNumber;
// Update maxSum and the corresponding indices if we have found a new max.
if (maxSum < currentSum) {
maxSum = currentSum;
maxStartIndex = currentStartIndex;
maxEndIndex = currentIndex;
}
// Reset currentSum and currentStartIndex if currentSum drops below 0.
if (currentSum < 0) {
currentSum = 0;
currentStartIndex = currentIndex + 1;
}
});
return inputArray.slice(maxStartIndex, maxEndIndex + 1);
}References
Practice
Prefix Sum
- Running Sum of 1d ArrayEasy
- Range Sum Query - ImmutableEasy
- Find Pivot IndexEasy
- Find the Highest AltitudeEasy
- Subarray Sum Equals KMedium
- Product of Array Except SelfMedium
- Continuous Subarray SumMedium
- Contiguous ArrayMedium
- Range Sum Query 2D - ImmutableMedium
- Subarray Sums Divisible by KMedium
- Maximum Size Subarray Sum Equals kMedium
- Corporate Flight BookingsMedium
- Car PoolingMedium
- Maximum SubarrayMedium
- Count of Range SumHard
Dynamic Programming
- Climbing StairsEasy
- Fibonacci NumberEasy
- Min Cost Climbing StairsEasy
- Pascal's TriangleEasy
- House RobberMedium
- House Robber IIMedium
- Coin ChangeMedium
- Coin Change IIMedium
- Longest Increasing SubsequenceMedium
- Longest Common SubsequenceMedium
- Word BreakMedium
- Unique PathsMedium
- Unique Paths IIMedium
- Minimum Path SumMedium
- Jump GameMedium
- Partition Equal Subset SumMedium
- Target SumMedium
- Decode WaysMedium
- Maximum Product SubarrayMedium
- Longest Palindromic SubstringMedium
- Palindromic SubstringsMedium
- Best Time to Buy and Sell Stock with CooldownMedium
- TriangleMedium
- Maximal SquareMedium
- Edit DistanceHard
- Regular Expression MatchingHard
- Burst BalloonsHard
- Distinct SubsequencesHard
- Longest Valid ParenthesesHard
- Best Time to Buy and Sell Stock IVHard
Spotted a mistake or something unclear? Report it on GitHub.