|
| 1 | +# BinarySearchTemplated Examples |
| 2 | + |
| 3 | +We will be utilising the BinarySearchTemplated implementation to solve some problems. |
| 4 | + |
| 5 | +The following will differ between problems: |
| 6 | +- Search Space |
| 7 | +- Condition |
| 8 | +- Returned Value |
| 9 | + |
| 10 | +<b> How do we know if a problem can be solved with this template? </b> |
| 11 | + |
| 12 | +A problem is well-suited for this approach when it meets the following criteria: |
| 13 | + |
| 14 | +- <b> Clear Distinction Between Categories: </b> There should be a definitive way to categorize elements into two |
| 15 | +groups: "pass" and "fail". |
| 16 | + |
| 17 | +- <b> Grouped Categories: </b> All the elements in one category should be grouped together without interspersion. This |
| 18 | +implies that if we move from a "fail" element to a "pass" one, all subsequent elements will be "pass". |
| 19 | + |
| 20 | +- <b> Monotonic Property: </b> There needs to be a consistent, unidirectional trend or property in the search space. |
| 21 | +This means the condition being checked either consistently increases or decreases. For the classical binary search on a |
| 22 | +sorted array, this monotonic property is the sorted order of the elements, where the array elements increase |
| 23 | +(or decrease) consistently. |
| 24 | + |
| 25 | +## 1. First Bad Version [Easy] |
| 26 | + |
| 27 | +### Problem: |
| 28 | + |
| 29 | +You are a product manager and currently leading a team to develop a new product. Unfortunately, the latest version of |
| 30 | +your product fails the quality check. Since each version is developed based on the previous version, all the versions |
| 31 | +after a bad version are also bad. |
| 32 | + |
| 33 | +Suppose you have `n` versions `[1, 2, ..., n]` and you want to find out the first bad one, which causes all the |
| 34 | +following ones to be bad. |
| 35 | + |
| 36 | +You are given an API bool `isBadVersion(version)` which returns whether `version` is bad. Implement a function to find |
| 37 | +the first bad version. You should minimize the number of calls to the API. |
| 38 | + |
| 39 | +Example 1: |
| 40 | +``` |
| 41 | +Input: n = 5, bad = 4 |
| 42 | +Output: 4 |
| 43 | +Explanation: |
| 44 | +call isBadVersion(3) -> false |
| 45 | +call isBadVersion(5) -> true |
| 46 | +call isBadVersion(4) -> true |
| 47 | +Then 4 is the first bad version. |
| 48 | +``` |
| 49 | + |
| 50 | +### Solution: |
| 51 | + |
| 52 | +<b> Search Space </b> |
| 53 | + |
| 54 | +The search space includes all possible values. |
| 55 | + |
| 56 | +```java |
| 57 | +high = n - 1; |
| 58 | +low = 0; |
| 59 | +``` |
| 60 | + |
| 61 | +<b> Condition </b> |
| 62 | + |
| 63 | +The condition has already been implemented. But we need to check if the elements that pass the condition will be on the |
| 64 | +right of those that fail! Since "all the versions after a bad version are also bad", our template will work. |
| 65 | + |
| 66 | +<b> Returned Value </b> |
| 67 | + |
| 68 | +Since we want to return the first bad version, we will return low. |
| 69 | + |
| 70 | + |
| 71 | + |
| 72 | +<b> Full Solution: </b> |
| 73 | + |
| 74 | +```java |
| 75 | +public int firstBadVersion(int n) { |
| 76 | + int high = n - 1; // max index for n elements |
| 77 | + int low = 0; |
| 78 | + while (low < high) { |
| 79 | + int mid = low + (high - low) / 2; |
| 80 | + if (isBadVersion(mid)) { // condition is isBadVersion |
| 81 | + high = mid; |
| 82 | + } else { |
| 83 | + low = mid + 1; |
| 84 | + } |
| 85 | + } |
| 86 | + |
| 87 | + return low; // return first "pass" |
| 88 | +} |
| 89 | +``` |
| 90 | + |
| 91 | +## 2. Capacity To Ship Packages Within D Days [Medium] |
| 92 | + |
| 93 | +### Problem: |
| 94 | + |
| 95 | +A conveyor belt has packages that must be shipped from one port to another within `days` days. |
| 96 | + |
| 97 | +The `ith` package on the conveyor belt has a weight of `weights[i]`. Each day, we load the ship with packages on the |
| 98 | +conveyor belt (in the order given by `weights`). We may not load more weight than the maximum weight capacity of the |
| 99 | +ship. |
| 100 | + |
| 101 | +Return the least weight capacity of the ship that will result in all the packages on the conveyor belt being shipped |
| 102 | +within `days` days. |
| 103 | + |
| 104 | +Example 1: |
| 105 | +``` |
| 106 | +Input: weights = [1,2,3,4,5,6,7,8,9,10], days = 5 |
| 107 | +Output: 15 |
| 108 | +Explanation: A ship capacity of 15 is the minimum to ship all the packages in 5 days like this: |
| 109 | +1st day: 1, 2, 3, 4, 5 |
| 110 | +2nd day: 6, 7 |
| 111 | +3rd day: 8 |
| 112 | +4th day: 9 |
| 113 | +5th day: 10 |
| 114 | +
|
| 115 | +Note that the cargo must be shipped in the order given, so using a ship of capacity 14 and splitting the packages into |
| 116 | +parts like (2, 3, 4, 5), (1, 6, 7), (8), (9), (10) is not allowed. |
| 117 | +``` |
| 118 | + |
| 119 | +### Solution: |
| 120 | + |
| 121 | +On first glance, while we are given an array, simply binary searching the array does not solve the problem. However, |
| 122 | +since our goal is to determine the least weight capacity of the ship, could this be our "target"? The answer is yes. |
| 123 | +By having each element being a least weight capacity case, with the lowest index being the heaviest weight and the |
| 124 | +highest index being the sum of all weights, we will have our search space. The monotonic property present is that if |
| 125 | +we can ship all packages within `D` days with capacity `m`, we can definitely ship them within `D` days with any |
| 126 | +capacity greater than `m`, such as `m + 1`. |
| 127 | + |
| 128 | +<b> Search Space </b> |
| 129 | + |
| 130 | +The search space is the range of least weight capacity we should search. The minimum least weight capacity that must be |
| 131 | +required is the heaviest package as we cannot split the heaviest package. The maximum least weight capacity that is |
| 132 | +necessary is the sum of all weights - i.e. we have to ship all packages within `1` day. |
| 133 | + |
| 134 | +```java |
| 135 | +int sum = 0; |
| 136 | +for (i = 0; i < weights.length; i++) { |
| 137 | + sum += weights[i]; |
| 138 | +} |
| 139 | +high = sum; |
| 140 | +low = weights[weights.length - 1]; |
| 141 | +``` |
| 142 | + |
| 143 | +<b> Condition </b> |
| 144 | + |
| 145 | +The condition is to check if it is feasible to ship all packages within `D` days with a least weight capacity of `m`. |
| 146 | +Where `D` is given and `m` is the mid-value in our search range. |
| 147 | + |
| 148 | +```java |
| 149 | +public static boolean isFeasible(int[] weights, int days, int capacity) { |
| 150 | + int daysNeeded = 1; |
| 151 | + int currentWeight = 0; |
| 152 | + for (int i = 0; i < weights.length; i++) { |
| 153 | + if (currentWeight + weights[i] > capacity) { |
| 154 | + daysNeeded++; |
| 155 | + currentWeight = 0; // Reset current weight as we are starting a new day |
| 156 | + } |
| 157 | + currentWeight += weights[i]; |
| 158 | + } |
| 159 | + return daysNeeded <= days; // If days needed is less than or equal to days, it is feasible |
| 160 | +} |
| 161 | +``` |
| 162 | + |
| 163 | +<b> Returned Value </b> |
| 164 | + |
| 165 | +Since we want to return the minimum least weight capacity where we can deliver in time, we will return low. |
| 166 | + |
| 167 | +<b> Full Solution: </b> |
| 168 | + |
| 169 | +```java |
| 170 | +public int shipWithinDays(int[] weights, int days) { |
| 171 | + public boolean isFeasible(int[] weights, int days, int capacity) { |
| 172 | + int daysNeeded = 1; |
| 173 | + int currentWeight = 0; |
| 174 | + for (int i = 0; i < weights.length; i++) { |
| 175 | + if (currentWeight + weights[i] > capacity) { |
| 176 | + daysNeeded++; |
| 177 | + currentWeight = 0; // Reset current weight as we are starting a new day |
| 178 | + } |
| 179 | + currentWeight += weights[i]; |
| 180 | + } |
| 181 | + return daysNeeded <= days; |
| 182 | + } |
| 183 | + |
| 184 | + int sum = 0; |
| 185 | + for (i = 0; i < weights.length; i++) { |
| 186 | + sum += weights[i]; |
| 187 | + } |
| 188 | + |
| 189 | + high = sum; |
| 190 | + low = weights[weights.length - 1]; |
| 191 | + |
| 192 | + while (low < high) { |
| 193 | + int mid = low + (high - low) / 2; |
| 194 | + if (isFeasible(weights, days, mid)) { // condition is isFeasible |
| 195 | + high = mid; |
| 196 | + } else { |
| 197 | + low = mid + 1; |
| 198 | + } |
| 199 | + } |
| 200 | + |
| 201 | + return low; // return first "pass" |
| 202 | +} |
| 203 | +``` |
0 commit comments