From 82f0b5edf04fba49d5fcff898e50142cac9d67a9 Mon Sep 17 00:00:00 2001 From: Oleksii Trekhleb Date: Fri, 4 Feb 2022 08:56:15 +0100 Subject: [PATCH] Add a Divide and Conquer version of the MaxSubArray problem. --- .npmrc | 2 +- .nvmrc | 1 + README.md | 3 ++ .../sets/maximum-subarray/README.md | 16 ++++++--- .../__test__/bfMaximumSubarray.test.js | 2 +- .../__test__/dcMaximumSubarraySum.test.js | 16 +++++++++ .../__test__/dpMaximumSubarray.test.js | 2 +- .../maximum-subarray/dcMaximumSubarraySum.js | 33 +++++++++++++++++++ 8 files changed, 67 insertions(+), 8 deletions(-) create mode 100644 .nvmrc create mode 100644 src/algorithms/sets/maximum-subarray/__test__/dcMaximumSubarraySum.test.js create mode 100644 src/algorithms/sets/maximum-subarray/dcMaximumSubarraySum.js diff --git a/.npmrc b/.npmrc index c0c80ba4..b6f27f13 100644 --- a/.npmrc +++ b/.npmrc @@ -1 +1 @@ -engine-strict=false +engine-strict=true diff --git a/.nvmrc b/.nvmrc new file mode 100644 index 00000000..958b5a36 --- /dev/null +++ b/.nvmrc @@ -0,0 +1 @@ +v14 diff --git a/README.md b/README.md index d949642e..6adff75d 100644 --- a/README.md +++ b/README.md @@ -207,6 +207,7 @@ algorithm is an abstraction higher than a computer program. * `B` [Best Time To Buy Sell Stocks](src/algorithms/uncategorized/best-time-to-buy-sell-stocks) - divide and conquer and one-pass examples * `A` [Permutations](src/algorithms/sets/permutations) (with and without repetitions) * `A` [Combinations](src/algorithms/sets/combinations) (with and without repetitions) + * `A` [Maximum Subarray](src/algorithms/sets/maximum-subarray) * **Dynamic Programming** - build up a solution using previously found sub-solutions * `B` [Fibonacci Number](src/algorithms/math/fibonacci) * `B` [Jump Game](src/algorithms/uncategorized/jump-game) @@ -278,6 +279,8 @@ rm -rf ./node_modules npm i ``` +Also make sure that you're using a correct Node version (`>=14.16.0`). If you're using [nvm](https://github.com/nvm-sh/nvm) for Node version management you may run `nvm use` from the root folder of the project and the correct version will be picked up. + **Playground** You may play with data-structures and algorithms in `./src/playground/playground.js` file and write diff --git a/src/algorithms/sets/maximum-subarray/README.md b/src/algorithms/sets/maximum-subarray/README.md index c152f54e..c65c5026 100644 --- a/src/algorithms/sets/maximum-subarray/README.md +++ b/src/algorithms/sets/maximum-subarray/README.md @@ -1,7 +1,7 @@ # 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 +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) @@ -10,11 +10,17 @@ 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 +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](./bfMaximumSubarray.js) +- Divide and Conquer solution `O(n^2)`: [dcMaximumSubarraySum.js](./dcMaximumSubarraySum.js) +- Dynamic Programming solution `O(n)`: [dpMaximumSubarray.js](./dpMaximumSubarray.js) + ## References - [Wikipedia](https://en.wikipedia.org/wiki/Maximum_subarray_problem) diff --git a/src/algorithms/sets/maximum-subarray/__test__/bfMaximumSubarray.test.js b/src/algorithms/sets/maximum-subarray/__test__/bfMaximumSubarray.test.js index e0faf0cb..6bff4271 100644 --- a/src/algorithms/sets/maximum-subarray/__test__/bfMaximumSubarray.test.js +++ b/src/algorithms/sets/maximum-subarray/__test__/bfMaximumSubarray.test.js @@ -1,7 +1,7 @@ import bfMaximumSubarray from '../bfMaximumSubarray'; describe('bfMaximumSubarray', () => { - it('should find maximum subarray using brute force algorithm', () => { + it('should find maximum subarray using the brute force algorithm', () => { expect(bfMaximumSubarray([])).toEqual([]); expect(bfMaximumSubarray([0, 0])).toEqual([0]); expect(bfMaximumSubarray([0, 0, 1])).toEqual([0, 0, 1]); diff --git a/src/algorithms/sets/maximum-subarray/__test__/dcMaximumSubarraySum.test.js b/src/algorithms/sets/maximum-subarray/__test__/dcMaximumSubarraySum.test.js new file mode 100644 index 00000000..3b3ace08 --- /dev/null +++ b/src/algorithms/sets/maximum-subarray/__test__/dcMaximumSubarraySum.test.js @@ -0,0 +1,16 @@ +import dcMaximumSubarray from '../dcMaximumSubarraySum'; + +describe('dcMaximumSubarraySum', () => { + it('should find maximum subarray sum using the divide and conquer algorithm', () => { + expect(dcMaximumSubarray([])).toEqual(-Infinity); + expect(dcMaximumSubarray([0, 0])).toEqual(0); + expect(dcMaximumSubarray([0, 0, 1])).toEqual(1); + expect(dcMaximumSubarray([0, 0, 1, 2])).toEqual(3); + expect(dcMaximumSubarray([0, 0, -1, 2])).toEqual(2); + expect(dcMaximumSubarray([-1, -2, -3, -4, -5])).toEqual(-1); + expect(dcMaximumSubarray([1, 2, 3, 2, 3, 4, 5])).toEqual(20); + expect(dcMaximumSubarray([-2, 1, -3, 4, -1, 2, 1, -5, 4])).toEqual(6); + expect(dcMaximumSubarray([-2, -3, 4, -1, -2, 1, 5, -3])).toEqual(7); + expect(dcMaximumSubarray([1, -3, 2, -5, 7, 6, -1, 4, 11, -23])).toEqual(27); + }); +}); diff --git a/src/algorithms/sets/maximum-subarray/__test__/dpMaximumSubarray.test.js b/src/algorithms/sets/maximum-subarray/__test__/dpMaximumSubarray.test.js index d8cf39d8..c00727cc 100644 --- a/src/algorithms/sets/maximum-subarray/__test__/dpMaximumSubarray.test.js +++ b/src/algorithms/sets/maximum-subarray/__test__/dpMaximumSubarray.test.js @@ -1,7 +1,7 @@ import dpMaximumSubarray from '../dpMaximumSubarray'; describe('dpMaximumSubarray', () => { - it('should find maximum subarray using dynamic programming algorithm', () => { + it('should find maximum subarray using the dynamic programming algorithm', () => { expect(dpMaximumSubarray([])).toEqual([]); expect(dpMaximumSubarray([0, 0])).toEqual([0]); expect(dpMaximumSubarray([0, 0, 1])).toEqual([0, 0, 1]); diff --git a/src/algorithms/sets/maximum-subarray/dcMaximumSubarraySum.js b/src/algorithms/sets/maximum-subarray/dcMaximumSubarraySum.js new file mode 100644 index 00000000..3ed9ab3e --- /dev/null +++ b/src/algorithms/sets/maximum-subarray/dcMaximumSubarraySum.js @@ -0,0 +1,33 @@ +/** + * 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); +}