From b128f2044345e881098cc5891aefc98acfc68082 Mon Sep 17 00:00:00 2001 From: Oleksii Trekhleb Date: Tue, 1 May 2018 11:16:08 +0300 Subject: [PATCH] Add maximum subarray. --- README.md | 3 +- .../sets/maximum-subarray/README.md | 19 ++++++ .../__test__/bfMaximumSubarray.test.js | 12 ++++ .../__test__/dpMaximumSubarray.test.js | 12 ++++ .../maximum-subarray/bfMaximumSubarray.js | 26 ++++++++ .../maximum-subarray/dpMaximumSubarray.js | 62 +++++++++++++++++++ 6 files changed, 133 insertions(+), 1 deletion(-) create mode 100644 src/algorithms/sets/maximum-subarray/README.md create mode 100644 src/algorithms/sets/maximum-subarray/__test__/bfMaximumSubarray.test.js create mode 100644 src/algorithms/sets/maximum-subarray/__test__/dpMaximumSubarray.test.js create mode 100644 src/algorithms/sets/maximum-subarray/bfMaximumSubarray.js create mode 100644 src/algorithms/sets/maximum-subarray/dpMaximumSubarray.js diff --git a/README.md b/README.md index 142b71da..d10fa6ba 100644 --- a/README.md +++ b/README.md @@ -42,6 +42,7 @@ * [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) * [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** * [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 @@ -100,7 +101,7 @@ * [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) * [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 * **Backtracking** * **Branch & Bound** diff --git a/src/algorithms/sets/maximum-subarray/README.md b/src/algorithms/sets/maximum-subarray/README.md new file mode 100644 index 00000000..b2448dee --- /dev/null +++ b/src/algorithms/sets/maximum-subarray/README.md @@ -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) diff --git a/src/algorithms/sets/maximum-subarray/__test__/bfMaximumSubarray.test.js b/src/algorithms/sets/maximum-subarray/__test__/bfMaximumSubarray.test.js new file mode 100644 index 00000000..520b5c01 --- /dev/null +++ b/src/algorithms/sets/maximum-subarray/__test__/bfMaximumSubarray.test.js @@ -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]); + }); +}); diff --git a/src/algorithms/sets/maximum-subarray/__test__/dpMaximumSubarray.test.js b/src/algorithms/sets/maximum-subarray/__test__/dpMaximumSubarray.test.js new file mode 100644 index 00000000..4ce30db8 --- /dev/null +++ b/src/algorithms/sets/maximum-subarray/__test__/dpMaximumSubarray.test.js @@ -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]); + }); +}); diff --git a/src/algorithms/sets/maximum-subarray/bfMaximumSubarray.js b/src/algorithms/sets/maximum-subarray/bfMaximumSubarray.js new file mode 100644 index 00000000..20c0b38e --- /dev/null +++ b/src/algorithms/sets/maximum-subarray/bfMaximumSubarray.js @@ -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); +} diff --git a/src/algorithms/sets/maximum-subarray/dpMaximumSubarray.js b/src/algorithms/sets/maximum-subarray/dpMaximumSubarray.js new file mode 100644 index 00000000..12f919d7 --- /dev/null +++ b/src/algorithms/sets/maximum-subarray/dpMaximumSubarray.js @@ -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; +}