diff --git a/book/config b/book/config index c95ecf80..95b431fb 160000 --- a/book/config +++ b/book/config @@ -1 +1 @@ -Subproject commit c95ecf80705c3c41e570c095574fa4c4affee732 +Subproject commit 95b431fb37af4b23a7ce17c183da7313f1d1acb4 diff --git a/book/content/part02/array.asc b/book/content/part02/array.asc index 8461f453..8050c390 100644 --- a/book/content/part02/array.asc +++ b/book/content/part02/array.asc @@ -300,12 +300,29 @@ To sum up, the time complexity of an array is: Many programming problems involve manipulating arrays. Here are some patterns that can help you improve your problem-solving skills. +(((Patterns, Two Pointers))) + ===== Two Pointers Pattern Usually, we use one pointer to navigate each element in an array. However, there are times when having two pointers (left/right, low/high) comes in handy. Let's do some examples. *AR-A*) _Given a sorted `array` of integers, find two numbers that add up to a `target` and return their values._ +.Function Signature +[source, javascript] +---- +/** + * Find two numbers that add up target. + * @param arr - The array of integers + * @param target - The target + * @returns {number[]} - array with the values that add up to target. + */ +function twoSum(arr, target) { + // give it a try on your own ... +} +---- + + .Examples [source, javascript] ---- @@ -335,7 +352,7 @@ We can use two pointers: one pointer starting from the left side and the other f Depending on whether the sum is bigger or smaller than the target, we move right or left. If the sum is equal to the target, we return the current left and right pointer's values. -.Solution 1: Two Pointers +.Solution 2: Two Pointers [source, javascript] ---- function twoSum(arr, target) { @@ -354,12 +371,30 @@ These two pointers have a runtime of `O(n)`. WARNING: This technique only works for sorted arrays. If the array was not sorted, you would have to sort it first or choose another approach. -===== Sliding Windows Pattern +(((Patterns, Sliding Window Pointers))) + +===== Sliding Window Pattern The sliding window pattern is similar to the two pointers. The difference is that the distance between the left and right pointer is always the same. Also, the numbers don't need to be sorted. Let's do an example! *AR-B*) _Find the max sum of an array of integers, only taking `k` items from the right and left side sequentially._ -_*Constraints*: `k` won't exceed the number of elements `n`: `1 <= k <= n`._ +**Constraints**: `k` won't exceed the number of elements in the array: `1 <= k <= n`. + +.Function Signature +[source, javascript] +---- +/** + * Find the max sum of an array of integers, + * only taking `k` items from the right and left side. + * + * @param {number[]} arr - The array of integers + * @param {number} k - The number of elements to sum up. + * @returns {number} + */ +function maxSum(arr, k) { + // Give it a try +}; +---- .Examples [source, javascript] @@ -370,23 +405,23 @@ maxSum([3, 10, 12, 4, 7, 2, 100, 1], 3); // 104 // (3 + 1 + 100 = 104) maxSum([1,200,1], 1); // 6 // (1 + 2 + 3 = 6) ---- -Let's take `[3, 10, 12, 4, 7, 2, 100, 1], k=3` as an example. - There are multiple ways to solve this problem. Before applying the sliding window, let's consider this other algorithm: *Backtracking algorithm* +Let's take `[3, 10, 12, 4, 7, 2, 100, 1], k = 3` as an example. + - We have two initial choices: going left with `3` or right with `1`. - We can take the first element from the left side `3`; from there, we can keep going left with `10` or right `1`. - If we go right with `1` on the right side, next, we have two options from the right side `100` or `10`. - If we go with `100`, then we compute the sum `3 + 1 + 100 = 104`. - Repeat with other combinations and keep track of the max sum. -How many combinations can we form? 2^k, since in the worst-case k is n, then we have a runtime of `2^n`! +How many combinations can we form? `2^k`, since in the worst-case `k` is equal to `n`, then we have a runtime of `O(2^n)`! // image::max-sum-backtracking.png[max sum with backtracking] -We can also visualize all the options as follows. If you add up the numbers from the top to bottom, you get the result for all combinations: +We can also visualize all the options as follows. If you add up the numbers from top to bottom, you get the result for all combinations: [graphviz, max-sum-sliding-window-red, png] .... @@ -438,7 +473,7 @@ graph G { .... -Notice that many elements on the middle branches (in red color) have the same numbers but in a different order, so the sums oscillate between 104 and 14. That's why this algorithm is not very optimal for this problem. +Notice that many middle branches (in red color) have the same numbers, but in a different order, so their sums oscillate between 104 and 14. That's why this algorithm is not very optimal for this problem. *Sliding window algorithm* @@ -450,6 +485,7 @@ image::max-sum-sliding-window.png[sliding window for arrays] Here's the implementation: +.Solution: using sliding window pointers [source, javascript] ---- function maxSum(arr, k) { @@ -470,7 +506,7 @@ function maxSum(arr, k) { The difference between the two pointers pattern and the sliding windows, it's that we move both pointers at the same time to keep the length of the window the same. -The runtime for this code is: `O(k)`. We move the window k times. Since `k <= n`, the final runtime is `O(n)`. +The runtime for this code is: `k`. As we move the window k times. Since `k <= n`, the final runtime is `O(n)`. ==== Practice Questions (((Interview Questions, Arrays))) diff --git a/book/images/max-sum-sliding-window-red.png b/book/images/max-sum-sliding-window-red.png new file mode 100644 index 00000000..ccbc1cb1 Binary files /dev/null and b/book/images/max-sum-sliding-window-red.png differ