diff --git a/README.md b/README.md index 633c8ed7..2c12f227 100644 --- a/README.md +++ b/README.md @@ -128,7 +128,7 @@ a set of rules that precisely define a sequence of operations. * `B` [Square Matrix Rotation](src/algorithms/uncategorized/square-matrix-rotation) - in-place algorithm * `B` [Jump Game](src/algorithms/uncategorized/jump-game) - backtracking, dynamic programming (top-down + bottom-up) and greedy examples * `B` [Unique Paths](src/algorithms/uncategorized/unique-paths) - backtracking, dynamic programming and Pascal's Triangle based examples - * `B` [Rain Terraces](src/algorithms/uncategorized/rain-terraces) - trapping rain water problem + * `B` [Rain Terraces](src/algorithms/uncategorized/rain-terraces) - trapping rain water problem (dynamic programming and brute force versions) * `A` [N-Queens Problem](src/algorithms/uncategorized/n-queens) * `A` [Knight's Tour](src/algorithms/uncategorized/knight-tour) @@ -140,6 +140,7 @@ algorithm is an abstraction higher than a computer program. * **Brute Force** - look at all the possibilities and selects the best solution * `B` [Linear Search](src/algorithms/search/linear-search) + * `B` [Rain Terraces](src/algorithms/uncategorized/rain-terraces) - trapping rain water problem * `A` [Maximum Subarray](src/algorithms/sets/maximum-subarray) * `A` [Travelling Salesman Problem](src/algorithms/graph/travelling-salesman) - shortest possible route that visits each city and returns to the origin city * **Greedy** - choose the best option at the current time, without any consideration for the future @@ -164,6 +165,7 @@ algorithm is an abstraction higher than a computer program. * `B` [Fibonacci Number](src/algorithms/math/fibonacci) * `B` [Jump Game](src/algorithms/uncategorized/jump-game) * `B` [Unique Paths](src/algorithms/uncategorized/unique-paths) + * `B` [Rain Terraces](src/algorithms/uncategorized/rain-terraces) - trapping rain water problem * `A` [Levenshtein Distance](src/algorithms/string/levenshtein-distance) - minimum edit distance between two sequences * `A` [Longest Common Subsequence](src/algorithms/sets/longest-common-subsequence) (LCS) * `A` [Longest Common Substring](src/algorithms/string/longest-common-substring) diff --git a/src/algorithms/uncategorized/rain-terraces/README.md b/src/algorithms/uncategorized/rain-terraces/README.md index e634bf36..d03548ef 100644 --- a/src/algorithms/uncategorized/rain-terraces/README.md +++ b/src/algorithms/uncategorized/rain-terraces/README.md @@ -62,16 +62,58 @@ be stored in every element of array. For example, consider the array `[3, 0, 0, 2, 0, 4]`, We can trap "3*2 units" of water between 3 an 2, "1 unit" on top of bar 2 and "3 units" between 2 and 4. See below diagram also. -A **simple solution** is to traverse every array element and find the highest -bars on left and right sides. Take the smaller of two heights. The difference -between smaller height and height of current element is the amount of water -that can be stored in this array element. Time complexity of this solution -is `O(n2)`. +### Approach 1: Brute force -An **efficient solution** is to pre-compute highest bar on left and right of -every bar in `O(n)` time. Then use these pre-computed values to find the -amount of water in every array element. +**Intuition** + +For each element in the array, we find the maximum level of water it can trap +after the rain, which is equal to the minimum of maximum height of bars on both +the sides minus its own height. + +**Steps** + +- Initialize `answer = 0` +- Iterate the array from left to right: + - Initialize `max_left = 0 and `max_right = 0` + - Iterate from the current element to the beginning of array updating: `max_left = max(max_left, height[j])` + - Iterate from the current element to the end of array updating: `max_right = max(max_right, height[j])` + - Add `min(max_left, max_right) − height[i]` to `answer` + +**Complexity Analysis** + +Time complexity: `O(n^2)`. For each element of array, we iterate the left and right parts. + +Auxiliary space complexity: `O(1)` extra space. + +### Approach 2: Dynamic Programming + +**Intuition** + +In brute force, we iterate over the left and right parts again and again just to +find the highest bar size up to that index. But, this could be stored. Voila, +dynamic programming. + +So we may pre-compute highest bar on left and right of every bar in `O(n)` time. +Then use these pre-computed values to find the amount of water in every array element. + +The concept is illustrated as shown: + +![DP Trapping Rain Water](https://leetcode.com/problems/trapping-rain-water/Figures/42/trapping_rain_water.png) + +**Steps** + +- Find maximum height of bar from the left end up to an index `i` in the array `left_max`. +- Find maximum height of bar from the right end up to an index `i` in the array `right_max`. +- Iterate over the `height` array and update `answer`: + - Add `min(max_left[i], max_right[i]) − height[i]` to `answer`. + +**Complexity Analysis** + +Time complexity: `O(n)`. + +Auxiliary space complexity: `O(n)` extra space. ## References - [GeeksForGeeks](https://www.geeksforgeeks.org/trapping-rain-water/) +- [LeetCode](https://leetcode.com/problems/trapping-rain-water/solution/) diff --git a/src/algorithms/uncategorized/rain-terraces/__test__/bfRainTerraces.test.js b/src/algorithms/uncategorized/rain-terraces/__test__/bfRainTerraces.test.js new file mode 100644 index 00000000..d11fc95e --- /dev/null +++ b/src/algorithms/uncategorized/rain-terraces/__test__/bfRainTerraces.test.js @@ -0,0 +1,21 @@ +import bfRainTerraces from '../bfRainTerraces'; + +describe('bfRainTerraces', () => { + it('should find the amount of water collected after raining', () => { + expect(bfRainTerraces([1])).toBe(0); + expect(bfRainTerraces([1, 0])).toBe(0); + expect(bfRainTerraces([0, 1])).toBe(0); + expect(bfRainTerraces([0, 1, 0])).toBe(0); + expect(bfRainTerraces([0, 1, 0, 0])).toBe(0); + expect(bfRainTerraces([0, 1, 0, 0, 1, 0])).toBe(2); + expect(bfRainTerraces([0, 2, 0, 0, 1, 0])).toBe(2); + expect(bfRainTerraces([2, 0, 2])).toBe(2); + expect(bfRainTerraces([2, 0, 5])).toBe(2); + expect(bfRainTerraces([3, 0, 0, 2, 0, 4])).toBe(10); + expect(bfRainTerraces([0, 1, 0, 2, 1, 0, 1, 3, 2, 1, 2, 1])).toBe(6); + expect(bfRainTerraces([1, 1, 1, 1, 1])).toBe(0); + expect(bfRainTerraces([1, 2, 3, 4, 5])).toBe(0); + expect(bfRainTerraces([4, 1, 3, 1, 2, 1, 2, 1])).toBe(4); + expect(bfRainTerraces([0, 2, 4, 3, 4, 2, 4, 0, 8, 7, 0])).toBe(7); + }); +}); diff --git a/src/algorithms/uncategorized/rain-terraces/__test__/dpRainTerraces.test.js b/src/algorithms/uncategorized/rain-terraces/__test__/dpRainTerraces.test.js new file mode 100644 index 00000000..50c3386c --- /dev/null +++ b/src/algorithms/uncategorized/rain-terraces/__test__/dpRainTerraces.test.js @@ -0,0 +1,21 @@ +import dpRainTerraces from '../dpRainTerraces'; + +describe('dpRainTerraces', () => { + it('should find the amount of water collected after raining', () => { + expect(dpRainTerraces([1])).toBe(0); + expect(dpRainTerraces([1, 0])).toBe(0); + expect(dpRainTerraces([0, 1])).toBe(0); + expect(dpRainTerraces([0, 1, 0])).toBe(0); + expect(dpRainTerraces([0, 1, 0, 0])).toBe(0); + expect(dpRainTerraces([0, 1, 0, 0, 1, 0])).toBe(2); + expect(dpRainTerraces([0, 2, 0, 0, 1, 0])).toBe(2); + expect(dpRainTerraces([2, 0, 2])).toBe(2); + expect(dpRainTerraces([2, 0, 5])).toBe(2); + expect(dpRainTerraces([3, 0, 0, 2, 0, 4])).toBe(10); + expect(dpRainTerraces([0, 1, 0, 2, 1, 0, 1, 3, 2, 1, 2, 1])).toBe(6); + expect(dpRainTerraces([1, 1, 1, 1, 1])).toBe(0); + expect(dpRainTerraces([1, 2, 3, 4, 5])).toBe(0); + expect(dpRainTerraces([4, 1, 3, 1, 2, 1, 2, 1])).toBe(4); + expect(dpRainTerraces([0, 2, 4, 3, 4, 2, 4, 0, 8, 7, 0])).toBe(7); + }); +}); diff --git a/src/algorithms/uncategorized/rain-terraces/__test__/rainTerraces.test.js b/src/algorithms/uncategorized/rain-terraces/__test__/rainTerraces.test.js deleted file mode 100644 index 5c4461b6..00000000 --- a/src/algorithms/uncategorized/rain-terraces/__test__/rainTerraces.test.js +++ /dev/null @@ -1,21 +0,0 @@ -import rainTerraces from '../rainTerraces'; - -describe('rainTerraces', () => { - it('should find the amount of water collected after raining', () => { - expect(rainTerraces([1])).toBe(0); - expect(rainTerraces([1, 0])).toBe(0); - expect(rainTerraces([0, 1])).toBe(0); - expect(rainTerraces([0, 1, 0])).toBe(0); - expect(rainTerraces([0, 1, 0, 0])).toBe(0); - expect(rainTerraces([0, 1, 0, 0, 1, 0])).toBe(2); - expect(rainTerraces([0, 2, 0, 0, 1, 0])).toBe(2); - expect(rainTerraces([2, 0, 2])).toBe(2); - expect(rainTerraces([2, 0, 5])).toBe(2); - expect(rainTerraces([3, 0, 0, 2, 0, 4])).toBe(10); - expect(rainTerraces([0, 1, 0, 2, 1, 0, 1, 3, 2, 1, 2, 1])).toBe(6); - expect(rainTerraces([1, 1, 1, 1, 1])).toBe(0); - expect(rainTerraces([1, 2, 3, 4, 5])).toBe(0); - expect(rainTerraces([4, 1, 3, 1, 2, 1, 2, 1])).toBe(4); - expect(rainTerraces([0, 2, 4, 3, 4, 2, 4, 0, 8, 7, 0])).toBe(7); - }); -}); diff --git a/src/algorithms/uncategorized/rain-terraces/bfRainTerraces.js b/src/algorithms/uncategorized/rain-terraces/bfRainTerraces.js new file mode 100644 index 00000000..f136e6d4 --- /dev/null +++ b/src/algorithms/uncategorized/rain-terraces/bfRainTerraces.js @@ -0,0 +1,33 @@ +/** + * BRUTE FORCE approach of solving Trapping Rain Water problem. + * + * @param {number[]} terraces + * @return {number} + */ +export default function bfRainTerraces(terraces) { + let waterAmount = 0; + + for (let terraceIndex = 0; terraceIndex < terraces.length; terraceIndex += 1) { + // Get left most high terrace. + let leftHighestLevel = 0; + for (let leftIndex = terraceIndex - 1; leftIndex >= 0; leftIndex -= 1) { + leftHighestLevel = Math.max(leftHighestLevel, terraces[leftIndex]); + } + + // Get right most high terrace. + let rightHighestLevel = 0; + for (let rightIndex = terraceIndex + 1; rightIndex < terraces.length; rightIndex += 1) { + rightHighestLevel = Math.max(rightHighestLevel, terraces[rightIndex]); + } + + // Add current terrace water amount. + const terraceBoundaryLevel = Math.min(leftHighestLevel, rightHighestLevel); + if (terraceBoundaryLevel > terraces[terraceIndex]) { + // Terrace will be able to store the water if the lowest of two left and right highest + // terraces are still higher than the current one. + waterAmount += Math.min(leftHighestLevel, rightHighestLevel) - terraces[terraceIndex]; + } + } + + return waterAmount; +} diff --git a/src/algorithms/uncategorized/rain-terraces/rainTerraces.js b/src/algorithms/uncategorized/rain-terraces/dpRainTerraces.js similarity index 95% rename from src/algorithms/uncategorized/rain-terraces/rainTerraces.js rename to src/algorithms/uncategorized/rain-terraces/dpRainTerraces.js index f2f15a9e..f4bb7c30 100644 --- a/src/algorithms/uncategorized/rain-terraces/rainTerraces.js +++ b/src/algorithms/uncategorized/rain-terraces/dpRainTerraces.js @@ -1,8 +1,10 @@ /** + * DYNAMIC PROGRAMMING approach of solving Trapping Rain Water problem. + * * @param {number[]} terraces * @return {number} */ -export default function rainTerraces(terraces) { +export default function dpRainTerraces(terraces) { /* * STEPS *