Add maximum subarray.

This commit is contained in:
Oleksii Trekhleb 2018-05-01 11:16:08 +03:00
parent 293b6f721f
commit b128f20443
6 changed files with 133 additions and 1 deletions

View File

@ -42,6 +42,7 @@
* [Longest Increasing subsequence](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/sets/longest-increasing-subsequence) * [Longest Increasing subsequence](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/sets/longest-increasing-subsequence)
* [Shortest Common Supersequence](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/sets/shortest-common-supersequence) (SCS) * [Shortest Common Supersequence](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/sets/shortest-common-supersequence) (SCS)
* [Knapsack Problem](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/sets/knapsack-problem) - "0/1" and "Unbound" ones * [Knapsack Problem](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/sets/knapsack-problem) - "0/1" and "Unbound" ones
* [Maximum Subarray](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/sets/maximum-subarray) - "Brute Force" and "Dynamic Programming" versions
* **String** * **String**
* [Levenshtein Distance](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/string/levenshtein-distance) - minimum edit distance between two sequences * [Levenshtein Distance](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/string/levenshtein-distance) - minimum edit distance between two sequences
* [Hamming Distance](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/string/hamming-distance) - number of positions at which the symbols are different * [Hamming Distance](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/string/hamming-distance) - number of positions at which the symbols are different
@ -100,7 +101,7 @@
* [Shortest Common Supersequence](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/sets/shortest-common-supersequence) * [Shortest Common Supersequence](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/sets/shortest-common-supersequence)
* [0/1 Knapsack Problem](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/sets/knapsack-problem) * [0/1 Knapsack Problem](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/sets/knapsack-problem)
* [Integer Partition](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/math/integer-partition) * [Integer Partition](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/math/integer-partition)
* Maximum subarray * [Maximum Subarray](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/sets/maximum-subarray)
* Maximum sum path * Maximum sum path
* **Backtracking** * **Backtracking**
* **Branch & Bound** * **Branch & Bound**

View File

@ -0,0 +1,19 @@
# Maximum subarray problem
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,
![Maximum subarray](https://wikimedia.org/api/rest_v1/media/math/render/svg/e8960f093107b71b21827e726e2bad8b023779b2)
## 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`.
## References
- [Wikipedia](https://en.wikipedia.org/wiki/Maximum_subarray_problem)
- [YouTube](https://www.youtube.com/watch?v=ohHWQf1HDfU)

View File

@ -0,0 +1,12 @@
import bfMaximumSubarray from '../bfMaximumSubarray';
describe('bfMaximumSubarray', () => {
it('should find maximum subarray using brute force algorithm', () => {
expect(bfMaximumSubarray([])).toEqual([]);
expect(bfMaximumSubarray([-1, -2, -3, -4, -5])).toEqual([-1]);
expect(bfMaximumSubarray([1, 2, 3, 2, 3, 4, 5])).toEqual([1, 2, 3, 2, 3, 4, 5]);
expect(bfMaximumSubarray([-2, 1, -3, 4, -1, 2, 1, -5, 4])).toEqual([4, -1, 2, 1]);
expect(bfMaximumSubarray([-2, -3, 4, -1, -2, 1, 5, -3])).toEqual([4, -1, -2, 1, 5]);
expect(bfMaximumSubarray([1, -3, 2, -5, 7, 6, -1, 4, 11, -23])).toEqual([7, 6, -1, 4, 11]);
});
});

View File

@ -0,0 +1,12 @@
import dpMaximumSubarray from '../dpMaximumSubarray';
describe('dpMaximumSubarray', () => {
it('should find maximum subarray using dynamic programming algorithm', () => {
expect(dpMaximumSubarray([])).toEqual([]);
expect(dpMaximumSubarray([-1, -2, -3, -4, -5])).toEqual([-1]);
expect(dpMaximumSubarray([1, 2, 3, 2, 3, 4, 5])).toEqual([1, 2, 3, 2, 3, 4, 5]);
expect(dpMaximumSubarray([-2, 1, -3, 4, -1, 2, 1, -5, 4])).toEqual([4, -1, 2, 1]);
expect(dpMaximumSubarray([-2, -3, 4, -1, -2, 1, 5, -3])).toEqual([4, -1, -2, 1, 5]);
expect(dpMaximumSubarray([1, -3, 2, -5, 7, 6, -1, 4, 11, -23])).toEqual([7, 6, -1, 4, 11]);
});
});

View File

@ -0,0 +1,26 @@
/**
* 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);
}

View File

@ -0,0 +1,62 @@
/**
* Dynamic Programming solution.
* Complexity: O(n)
*
* @param {Number[]} inputArray
* @return {Number[]}
*/
export default function dpMaximumSubarray(inputArray) {
// Check if all elements of inputArray are negative ones and return the highest
// one in this case.
let allNegative = true;
let highestElementValue = null;
for (let i = 0; i < inputArray.length; i += 1) {
if (inputArray[i] >= 0) {
allNegative = false;
}
if (highestElementValue === null || highestElementValue < inputArray[i]) {
highestElementValue = inputArray[i];
}
}
if (allNegative && highestElementValue !== null) {
return [highestElementValue];
}
// Let's assume that there is at list one positive integer exists in array.
// And thus the maximum sum will for sure be grater then 0. Thus we're able
// to always reset max sum to zero.
let maxSum = 0;
// This array will keep a combination that gave the highest sum.
let maxSubArray = [];
// Current sum and subarray that will memoize all previous computations.
let currentSum = 0;
let currentSubArray = [];
for (let i = 0; i < inputArray.length; i += 1) {
// Let's add current element value to the current sum.
currentSum += inputArray[i];
if (currentSum < 0) {
// If the sum went below zero then reset it and don't add current element to max subarray.
currentSum = 0;
// Reset current subarray.
currentSubArray = [];
} else {
// If current sum stays positive then add current element to current sub array.
currentSubArray.push(inputArray[i]);
if (currentSum > maxSum) {
// If current sum became greater then max registered sum then update
// max sum and max subarray.
maxSum = currentSum;
maxSubArray = currentSubArray.slice();
}
}
}
return maxSubArray;
}