|
| 1 | +# BinarySearchTemplated |
| 2 | + |
| 3 | +## Background |
| 4 | + |
| 5 | +BinarySearchTemplated is a more generalised algorithm of [BinarySearch](../binarySearch) that removes the condition that |
| 6 | +checks if the current mid-value is equal to the target (which helps to end the search the moment the target is found). |
| 7 | +The template adds a "condition" method which will be modified based on the requirements of the implementation. |
| 8 | + |
| 9 | +The narrowing of the search space differs from BinarySearch - only the high pointer will be adjusted by one unit. |
| 10 | + |
| 11 | +This template will work for most binary search problems and will require the following changes when used for different |
| 12 | +problems: |
| 13 | +- Search space (high and low pointers) |
| 14 | +- Condition method |
| 15 | +- Returned value (low or low - 1) |
| 16 | + |
| 17 | +### Search Space (Requires change) |
| 18 | +Initialise the boundary values of the high and low pointers to include all possible elements in the search space. |
| 19 | + |
| 20 | +### Condition (Requires change) |
| 21 | +We assume that when the condition returns true, the current value "passes" and when the condition returns false, the |
| 22 | +current value "fails". |
| 23 | + |
| 24 | +Note that in this template, the conditional blocks |
| 25 | +``` |
| 26 | +if (condition(x)) { |
| 27 | + high = mid; |
| 28 | +} else { |
| 29 | + low = mid + 1; |
| 30 | +} |
| 31 | +``` |
| 32 | +requires elements that "fail" the condition to be on the left of the elements that "pass" the condition, see below, in a |
| 33 | +sorted array due to the way the high and low pointers are reassigned. |
| 34 | + |
| 35 | + |
| 36 | + |
| 37 | +Hence, we will need to implement a condition method that is able to discern between arrays that "pass" and "fail" |
| 38 | +accurately and also place them in the correct relative positions i.e. "fail" on the left of "pass". Suppose we change |
| 39 | +the condition method implementation in BinarySearchTemplated from `value >= target` to `value <= target`, what will |
| 40 | +happen? |
| 41 | +<details> |
| 42 | +<summary> <b>what will happen?</b> </summary> |
| 43 | +The array becomes "P P F F F F" and the low and high pointers are now reassigned wrongly. We are now looking for the |
| 44 | +first "fail" instead of the first "pass". |
| 45 | + |
| 46 | +To resolve this issue, multiple changes are required: the pointer assignment bodies have to be swapped, low = mid + 1 |
| 47 | +needs to be changed to low = mid, high = mid changed to high = mid - 1 AND ceiling division has to be used to calculate |
| 48 | +the mid-value. The arrangement of the "pass" elements on the left of the "fail" elements is discouraged as it breaks |
| 49 | +away from the convention used in the template. |
| 50 | +</details> |
| 51 | + |
| 52 | +### Returned Value (Requires change) |
| 53 | +In this implementation of BinarySearchTemplated, we return the first "pass" in the array with `return low`. This is |
| 54 | +because our condition method implementation encompasses the target value that we are finding i.e. when |
| 55 | +`value == target`. |
| 56 | + |
| 57 | +```java |
| 58 | +public static boolean condition(int value, int target) { |
| 59 | + return value >= target; |
| 60 | +} |
| 61 | +``` |
| 62 | + |
| 63 | + |
| 64 | +However, if we want to return the last "fail" in the array, we will `return low - 1`. |
| 65 | + |
| 66 | +Suppose now we modify the condition to be `value > target`, how can we modify our BinarySearchTemplated to still work as |
| 67 | +expected? |
| 68 | +<details> |
| 69 | +<summary> <b>value > target?</b> </summary> |
| 70 | +Replace `return low` with `return low - 1` and replace arr[low] with arr[low - 1] as now the target value is the last |
| 71 | +"fail". |
| 72 | +</details> |
| 73 | + |
| 74 | + |
| 75 | +### Search Space Adjustment |
| 76 | +What should be the search space adjustment? Why is only low reassigned with an increment and not high? |
| 77 | + |
| 78 | +Due to the nature of floor division in Java's \ operator, if there are two mid-values within the search range, which is |
| 79 | +when the number of elements is even, the first mid-value will be selected. Suppose we do not increment the low pointer |
| 80 | +during reassignment, `low = mid`, let us take a look at the following example: |
| 81 | + |
| 82 | + |
| 83 | + |
| 84 | +The search space has been narrowed down to the range of index 1 (low) to 2 (high). The mid-value is calculated, |
| 85 | +`mid = (1 + 2) / 2`, to be 1 due to floor division. Since `2 < 5`, we enter the else block where there is reassignment |
| 86 | +of `low = mid`. This means that the low pointer is still pointing to index 1 and the high pointer remains unchanged at |
| 87 | +index 2. This results in an infinite loop as the search range is not narrowed down. |
| 88 | + |
| 89 | +To resolve this issue, we need `low = mid + 1`, which will result in the low pointer pointing to index 2 in this |
| 90 | +scenario. We still ensure correctness because the mid-value is not the target value, as the mid-value < target, and we |
| 91 | +can safely exclude it from the search range. |
| 92 | + |
| 93 | +Why should we not decrement the high pointer during reassignment? This is because the mid-value could be the target |
| 94 | +as the condition implemented is `value >= target`, hence, we cannot exclude it from the search range. |
| 95 | + |
| 96 | +Note that if ceiling division is used instead, `high = mid - 1` will be required to prevent an infinite loop. Both |
| 97 | +`low = mid + 1` and `low = mid` will work in this case. |
| 98 | + |
| 99 | +See [here](binarySearchTemplatedExamples/README.md) to use the template for other problems. |
| 100 | + |
| 101 | +Credits: [Powerful Ultimate Binary Search Template](https://leetcode.com/discuss/general-discussion/786126/python-powerful-ultimate-binary-search-template-solved-many-problems) |
| 102 | + |
| 103 | + |
| 104 | +## Complexity Analysis |
| 105 | +**Time**: |
| 106 | +- Worst case: O(log n) |
| 107 | +- Average case: O(log n) |
| 108 | +- Best case: O(log n) |
| 109 | + |
| 110 | +In all cases, O(log n) iterations will be required as there is no condition to exit the loop prematurely. |
| 111 | + |
| 112 | +**Space**: O(1) |
| 113 | + |
| 114 | +Since no new data structures are used and searching is only done within the array given. |
0 commit comments