|
1 |
| -## Arrays |
2 |
| -An **array** is a data structure that holds a fixed number of objects. Because arrays have fixed sizes, they are highly efficient for quick lookups regardless of how big the array is. However, there is a tradeoff with this fast access time; any insertion or deletion from the middle of the array requires moving the rest of the elements to fill in or close the gap. To optimize time efficiency, try to add and delete mostly from the end of the array. |
| 1 | +The **two pointer method** is a helpful technique to always keep in mind when working with strings and arrays questions. It's a clever optimization that can help reduce time complexity with no added space complexity (a win-win!) by utilizing extra pointers to avoid repetitive operations. |
3 | 2 |
|
4 |
| -Arrays commonly come up in interviews, so it's important to review the array library for the language you code in. |
| 3 | +This approach is best demonstrated through a walkthrough, as done below. |
5 | 4 |
|
6 |
| -**Tips:** |
7 |
| -* Off-by-one errors can often happen with arrays, so be wary of potentially over indexing as it will throw an error |
8 |
| -* Try to add elements to the back of an array instead of the front, as adding to the front requires shifting every element back |
9 |
| -* In Java, arrays are a fixed size so consider utilizing an [ArrayList](https://docs.oracle.com/javase/8/docs/api/java/util/ArrayList.html) instead if you need to dynamically alter the size of the array. |
| 5 | +## Problem: Minimum size subarray sum |
10 | 6 |
|
11 |
| -## Strings |
12 |
| -**Strings** are a special kind of array, one that only contains characters. They commonly come up in interview questions, so it's important to go through the string library for the language you're most comfortable with. You should know common operations such as: getting the length, getting a substring, splitting a string based on a delimiter, etc. |
| 7 | +Given an array of n positive integers and a positive integer s, find the minimal length of a contiguous subarray of which the sum ≥ s. If there isn't one, return 0 instead. |
13 | 8 |
|
14 |
| -It's important to note that whenever you mutate a string, a new copy of the string is created. There are different ways to reduce the space utilized depending on the language: |
15 |
| -* In Python, you can represent a string as a list of characters and operate on the list of character instead. |
16 |
| -* In Java, you can utilize the [StringBuffer](https://docs.oracle.com/javase/7/docs/api/java/lang/StringBuffer.html) class to mitigate the amount of space utilized if you need to mutate a string. |
| 9 | +**Example:** |
| 10 | +```python |
| 11 | +>>> min_sub_array_length([2,3,1,2,4,3], 7) |
| 12 | +2 |
| 13 | +``` |
17 | 14 |
|
18 |
| -## Patterns List |
19 |
| -* [Two pointer](https://guides.codepath.com/compsci/Two-pointer) |
20 |
| -* [Binary Search](https://guides.codepath.com/compsci/Binary-Search) |
| 15 | +Explanation: the subarray [4,3] has the minimal length under the problem constraint. |
21 | 16 |
|
22 |
| -### Strings |
23 |
| -#### General guide |
24 |
| -* [Coding for Interviews Strings Guide](http://blog.codingforinterviews.com/string-questions/) |
| 17 | +## Solution #1: Brute Force |
| 18 | +**Approach** |
25 | 19 |
|
26 |
| -#### Strings in C++ |
27 |
| - * [Character Arrays in C++](https://www.youtube.com/watch?v=Bf8a6IC1dE8) |
28 |
| - * [Character Arrays in C++ Part 2](https://www.youtube.com/watch?v=vFZTxvUoZSU) |
| 20 | +When first given a problem, if an optimal solution is not immediately clear, it's better to have a solution that works rather than to be stuck. With this problem, a brute force solution would be to generate all possible subarrays and find the length of the shortest subarray that sums up to a sum that is greater than or equal to the given number. |
29 | 21 |
|
30 |
| -### Arrays |
31 |
| -#### General guide |
32 |
| - * [InterviewCake Arrays](https://www.interviewcake.com/concept/java/array) |
| 22 | +**Implementation** |
| 23 | +```python |
| 24 | +def min_sub_array_length(nums, sum): |
| 25 | + min_length = float("inf") |
| 26 | + for start_idx in range(len(nums)): |
| 27 | + for end_idx in range(start_idx, len(nums)): |
| 28 | + subarray_sum = get_sum(nums, start_idx, end_idx) |
| 29 | + if subarray_sum >= sum: |
| 30 | + min_length = min(min_length, end_idx - start_idx + 1) |
| 31 | + return min_length if min_length != float("inf") else 0 |
| 32 | + |
| 33 | +def get_sum(nums, start_index, end_index): |
| 34 | + result = 0 |
| 35 | + for i in range(start_index, end_index + 1): |
| 36 | + result += nums[i] |
| 37 | + return result |
| 38 | +``` |
| 39 | + |
| 40 | +**Time and space complexity** |
| 41 | + |
| 42 | +The time complexity of this solution would be O(N<sup>3</sup>). The double for loop results in O(N<sup>2</sup>) calls to get_sum and each call to get_sum has a worst case run time of O(N), which results in a O(N<sup>2</sup> * N) = **O(N<sup>3</sup>) runtime**. |
| 43 | + |
| 44 | +The space complexity would be **O(1)** because the solution doesn't create new data structures. |
| 45 | + |
| 46 | +## Improvements |
| 47 | +#### Optimization #1: Keep track of a running sum instead of running `get_sum` in each iteration of the inner `end_idx` for loop |
| 48 | +In the brute solution, a lot of repetitive calculations are done in the inner `end_idx` for loop with the `get_sum` function. Instead of having to recalculate the sum from elements `start_idx` to `end_idx` within every iteration of the `end_idx` loop, we can store a `subarray_sum` variable to store calculations from previous iterations and simply add to it within each iteration of the `end_idx` loop. |
| 49 | + |
| 50 | +```python |
| 51 | +def min_sub_array_length(nums, sum): |
| 52 | + min_length = float("inf") |
| 53 | + for start_idx in range(len(nums)): |
| 54 | + subarray_sum = 0 |
| 55 | + for end_idx in range(start_idx, len(nums)): |
| 56 | + subarray_sum += nums[end_idx] |
| 57 | + if subarray_sum >= sum: |
| 58 | + min_length = min(min_length, end_idx - start_idx + 1) |
| 59 | + return min_length if min_length != float("inf") else 0 |
| 60 | +``` |
| 61 | + |
| 62 | +This optimization reduces the time complexity from O(N<sup>3</sup>) to O(N<sup>2</sup>) with the addition of a variable to store the accumulating sum. |
| 63 | + |
| 64 | + |
| 65 | +#### Optimization #2: Reduce number of calculations by terminating the inner `end_idx` for loop early |
| 66 | +With the improved solution, we can further reduce the number of iterations in the inner for loop by terminating it early. Once we have a `subarray_sum` that is equal to or greater than the target sum, we can simply move to the next iteration of the outer for loop. This is because the questions asks for minimum length subarray and any further iterations of the inner for loop would only cause an increase in the subarray length. |
| 67 | + |
| 68 | +```python |
| 69 | +def min_sub_array_length(nums, sum): |
| 70 | + min_length = float("inf") |
| 71 | + for start_idx in range(len(nums)): |
| 72 | + subarray_sum = 0 |
| 73 | + for end_idx in range(start_idx, len(nums)): |
| 74 | + subarray_sum += nums[end_idx] |
| 75 | + if subarray_sum >= sum: |
| 76 | + min_length = min(min_length, end_idx - start_idx + 1) |
| 77 | + continue |
| 78 | + return min_length if min_length != float("inf") else 0 |
| 79 | +``` |
| 80 | + |
| 81 | +This is a minor time complexity improvement and this solution will still have a worst case runtime of O(N<sup>2</sup>). The improvement is nice, but to reduce the runtime from O(N<sup>2</sup>) to O(N), we would need to somehow eliminate the inner for loop. |
| 82 | + |
| 83 | + |
| 84 | +## Solution #2: Two pointer approach |
| 85 | +**Approach** |
| 86 | +The optimal, two pointer approach to this problem utilizing the observations we made in the previous section. The main idea of this approach is that we grow and shrink an interval as we loop through the list while keeping a running sum that we update as we alter the interval. |
| 87 | + |
| 88 | +There will be two pointers, one to track the start of the interval and the other to track the end. They will both start at the beginning of the list and will dynamically move to the right until we hit the end of the list. |
| 89 | + |
| 90 | +First, we grow the interval to the right until it exceeds the minimum sum. Once we find that interval, we move the start pointer right as much as we can to shrink the interval until it sums up to a number that is smaller than the sum. |
| 91 | + |
| 92 | +Then, we move the end pointer to once again to try and hit the sum with new intervals. If growing the interval by moving the end pointer leads to an interval that sums up to at least the target sum, we need to repeat the process of trying to shrink the interval again by moving the start pointer before further moving the end pointer. |
| 93 | + |
| 94 | +As we utilize these two pointers to determine which intervals to evaluate, we have a variable to keep track of the current sum of the interval as we go along to avoid recalculating it every time one of the pointers moves to the right and another variable to store the length of the shortest interval that sums up to >= the target sum. |
| 95 | + |
| 96 | +This push and pull of the end and start pointer will continue until we finish looping through the list. |
| 97 | + |
| 98 | + |
| 99 | +**Implementation** |
| 100 | +```python |
| 101 | +def min_sub_array_length(nums, sum): |
| 102 | + start_idx = 0 |
| 103 | + min_length, subarray_sum = float('inf'), 0 |
| 104 | + |
| 105 | + for end_idx in range(len(nums)): |
| 106 | + subarray_sum += nums[end_idx] |
| 107 | + while subarray_sum >= sum: |
| 108 | + min_length = min(min_length, end_idx - start_idx + 1) |
| 109 | + subarray_sum -= nums[start_idx] |
| 110 | + start_idx += 1 |
| 111 | + if min_length == float('inf'): |
| 112 | + return 0 |
| 113 | + return min_length |
| 114 | +``` |
| 115 | + |
| 116 | +**Time and space complexity** |
| 117 | + |
| 118 | +The time complexity of this solution would be **O(N)** because each element is at most visited twice. In the worst case scenario, all elements will be visited once by the start pointer and another time by the end pointer. |
| 119 | + |
| 120 | +The space complexity would be **O(1)** because the solution doesn't create new data structures. |
| 121 | + |
| 122 | +**Walkthrough** |
| 123 | + |
| 124 | +Take the example of `min_sub_array_length([2,3,1,2,4,3], 7)`. The left pointer starts at 0 and the right doesn't exist yet. |
| 125 | + |
| 126 | +As we start looping through the list, our first interval is [2]. We won't fulfill the while loop condition until the list reaches [2, 3, 1, 2] whose sum, 8 is >= 7. We then set the `min_length` to 4. |
| 127 | + |
| 128 | +Now, we shrink the interval to [3, 1, 2] by increasing `start_idx` by 1. This new interval sums up to less than the target sum, 7 so we need to grow the interval. In the next iteration, we grow the interval to [3, 1, 2, 4], which has a sum of 10 and once again, we satisfy the while loop condition. |
| 129 | + |
| 130 | +We then shrink the interval to [1, 2, 4]. This is the shortest interval we've come across that sums up to at least the target sum, so we update the `min_length` to 3. |
| 131 | + |
| 132 | +We now move the `end_idx` pointer and it hits the end of the list, with interval [2, 4, 3]. Then shrink the interval to [4, 3], which sums up to 7, the target sum. This is the shortest interval we've come across that sums up to at least the target sum, so we update the `min_length` to 2. This is the final result that is returned. |
| 133 | + |
| 134 | +## Takeaways |
| 135 | + |
| 136 | +This optimization can often be applied to improve solutions that involve the use of multiple for loops, as shown in the example above. If you have an approach that utilizes multiple for loops, analyze the actions within those for loops to determine if repetitive calculations can be removed through strategic movements of multiple pointers. |
| 137 | + |
| 138 | +**Note**: Though this walkthrough demonstrated applying the two pointer approach to an arrays problem, this approach is commonly utilized to solve string problems as well. |
33 | 139 |
|
34 |
| -#### Python arrays |
35 |
| -* [Google developer lists guide](https://developers.google.com/edu/python/lists) |
36 |
| -#### Java arrays |
37 |
| - * [InterviewCake DynamicArray](https://www.interviewcake.com/concept/java/dynamic-array-amortized-analysis?) |
38 |
| - * [ArrayList Succinctly Guide](https://code.tutsplus.com/tutorials/the-array-list--cms-20661) |
|
0 commit comments