---
# System prepended metadata

title: 07. Binary Search
tags: [LeetCode筆記]

---

# Binary Search

###### tags: `LeetCode筆記`

* **Target** - the value that you are searching for
* **Index** - the current location that you are searching
* **Left, Right** - the indicies from which we use to maintain our search Space
* **Mid** - the index that we use to apply a condition to determine if we should search left or right

### Note:
* Binary Search can take many alternate forms and might **not always be as straight forward as searching for a specific value**. 
* Sometimes you will have to apply **a specific condition or rule to determine which side** (left or right) to search next.

## :memo: 一、基本概念

### 組成
1. Pre-processing - Sort if collection is unsorted.
2. Binary Search - Using a loop or recursion to divide search space in half after each comparison.
3. Post-processing - Determine viable candidates in the remaining space.

### 思考
為何code寫得不一樣?
哪一個較簡單?
哪一個較佳?
*主程式最後一行結束時要回傳left還是right?


## :memo: 二、Template

### Template #1 最基本

起始條件: **left = 0, right = length - 1**
終止條件: **left > right**
向左找: **right = mid - 1**
向右找: **left = mid + 1**
```
int left = 0;
int right = numsSize - 1;
int mid = 0;

while (left <= right)  //是小於等於
{
    mid = (left + right) / 2;
    if (nums[mid] == target)
    {
        return mid;
    }
    else if (nums[mid] > target)
    {
        right = mid - 1;
    }
    else if (nums[mid] < target)
    {
        left = mid + 1;
    }
}
    
return -1;
```

**範例: Sqrt(x)**
Constraints:
0 <= x <= 2^31 - 1
```
int mySqrt(int x) {
    int left = 0;
    int right = x;
    long mid = 0;  //要用long
    
    while (left <= right)
    {
        mid = (left + right) / 2;
        long mid_square = mid * mid;
        if (mid_square == x)
        {
            return mid;
        }
        else if (mid_square < x)
        {
            left = mid + 1;
        }
        else
        {
            right = mid - 1;
        }
    }
    
    return right;  //要回傳right才對
}
```
**注意:** 上面這一題範例要回傳**right**才對

**數學方法:** Newton's Method (Best)
```
class Solution {
    public int mySqrt(int x) {
        if (x < 2) return x;

        double x0 = x;
        double x1 = (x0 + x / x0) / 2.0;
        while (Math.abs(x0 - x1) >= 1) {
            x0 = x1;
            x1 = (x0 + x / x0) / 2.0;
        }

        return (int) x1;
    }
}
```

### Template #2 高機率在右邊?(我猜想)

起始條件: left = 0, right = length - 1
終止條件: **left == right**
向左找: right = mid
向右找: **left = mid + 1**
* Search Condition needs to access the element's immediate right neighbor
* Use the element's right neighbor to determine if the condition is met and decide whether to go left or right
* Guarantees Search Space is at least 2 in size at each step
* Post-processing required. oop/Recursion ends when you have 1 element left. Need to assess if the remaining element meets the condition.
```
if (numsSize == 0) {
    return -1;
}
int left = 0
int right = numsSize - 1;
while (left < right)
{
    // Prevent (left + right) overflow
    int mid = left + (right - left) / 2;
    if (nums[mid] == target)
    { 
        return mid;
    }
    else if (nums[mid] < target)
    { 
        left = mid + 1;
    }
    else
    { 
        right = mid; 
    }
}

// Post-processing:
// End Condition: left == right
if (left != numsSize() && nums[left] == target)
{
    return left;
}

return -1;
```

**範例: First Bad Version**

![](https://i.imgur.com/u6QfV6o.png)

Since each version is developed based on the previous version, all the versions after a bad version are also bad.
```
int left = 1;
int right = n;
int mid = 0;

while (left < right)
{
    mid = left + (right - left) / 2;
    //printf("l:%d m:%d r:%d\n",left,mid,right);
    if (isBadVersion(mid) == false)
    {
        left = mid + 1;
    }
    else if (isBadVersion(mid) == true)
    {
        right = mid;
    }        
}

if (left != n && isBadVersion(left) == true)
{
    return left;
}

return right;  //不能寫-1
```

### Template #3 在左或在右機率一樣?(我猜想)

起始條件: left = 0, right = length - 1
終止條件: **left + 1 == right**
向左找: right = mid
向右找: **left = mid**
* Search Condition needs to access element's immediate left and right neighbors
* Use element's neighbors to determine if condition is met and decide whether to go left or right
* Gurantees Search Space is at least 3 in size at each step
* Post-processing required. Loop/Recursion ends when you have 2 elements left. Need to assess if the remaining elements meet the condition.
```
int left = 0;
int right = numsSize() - 1;
while (left + 1 < right)
{
    // Prevent (left + right) overflow
    int mid = left + (right - left) / 2;
    if (nums[mid] == target)
    {
        return mid;
    }
    else if (nums[mid] < target)
    {
        left = mid;
    }
    else
    {
        right = mid;
    }
}

// Post-processing:
// End Condition: left + 1 == right
if (nums[left] == target)
{
    return left;
}
if (nums[right] == target)
{
    return right;
}

return -1;
```

## :memo: Guess Number Higher or Lower (Easy)
![](https://i.imgur.com/xuKcAtC.png)

![](https://i.imgur.com/8BCNdes.png)

### 作法
變數都要用 long long 不然不夠大
```
/** 
 * Forward declaration of guess API.
 * @param  num   your guess
 * @return 	     -1 if num is higher than the picked number
 *			      1 if num is lower than the picked number
 *               otherwise return 0
 * int guess(int num);
 */

class Solution {
public:
    int guessNumber(int n) {
        long long left = 1;
        long long right = n;
        long long mid = 0;
        
        while (left <= right) {
            mid = (left + right) / 2;
            
            if (guess(mid) == 0) {
                return mid;
            }
            else if (guess(mid) == 1) {
                left = mid + 1;
            }
            else {
                right = mid - 1;
            }
        }
        
        return right;
    }
};
```

## :memo: Closest Binary Search Tree Value (Easy)
Given the root of a binary search tree and a target value, return the value in the BST that is closest to the target.

![](https://i.imgur.com/ewaQJm2.png)

![](https://i.imgur.com/Smg4GhQ.png)

### 作法 Traverse
**時間: O(N)**
```
class Solution {
public:
    void traverse(TreeNode* root, double target, double* diff_min, int* ans) {
        if (root == nullptr) {
            return;
        }
        
        if (root->val == target) {
            diff_min = 0;
            *ans = target;
            
            return;
        }
        
        if (abs(root->val - target) < *diff_min) {
            *diff_min = abs(root->val - target);
            *ans = root->val;
        }
        
        if (target <= root->val) {
            traverse(root->left, target, diff_min, ans);
        }
        else if (target > root->val) {
            traverse(root->right, target, diff_min, ans);
        }
        
        return;
    }
    int closestValue(TreeNode* root, double target) {
        double diff_min = INT_MAX;
        int ans = root->val;
        
        traverse(root, target, &diff_min, &ans);
        
        return ans;
    }
};
```

### 作法 Binary Search
**時間: O(H)**
```
class Solution {
public:
    int closestValue(TreeNode* root, double target) {
        int val, closest = root->val;

        while (root != nullptr) {
            val = root->val;
            closest = abs(val - target) < abs(closest - target) or (abs(val - target) == abs(closest - target) and val < closest) ? val : closest;
            root = target < val ? root->left : root->right;
        }
        
        return closest;
    }
};
```

## :memo: *Valid Perfect Square (Easy)
![](https://i.imgur.com/FUW7hzE.png)

### 作法
if num == 1 要回 true
```
class Solution {
public:
    bool isPerfectSquare(long long num) {
        if (num == 1) {
            return true;
        }
        
        long long left = 1;
        long long right = num / 2;
        long long mid = 0;
        
        while (left <= right) {
            mid = (left + right) / 2;
            if (mid * mid == num) {
                return true;
            }
            else if (mid * mid < num) {
                left = mid + 1;
            }
            else {
                right = mid - 1;
            }
        }
        
        return false;
    }
};
```

## :memo: Find Smallest Letter Greater Than Target (Easy)
![](https://i.imgur.com/BofF1ay.png)

### 作法
在使用template II後要做後處理，檢查left的位置在尾端還是在中間而且letters[left]要小於等於target
```
class Solution {
public:
    char nextGreatestLetter(vector<char>& letters, char target) {
        int left = 0;
        int right = letters.size() - 1;
        int mid = 0;
        
        while (left < right) {
            int mid = left + (right - left) / 2;
            if (letters[mid] <= target) {
                left = mid + 1;
            }
            else {
                right = mid;
            }
        }
        
        // 要做 post-process
        if (left < letters.size() - 1 && letters[left] <= target) {
            return letters[left + 1];
        }
        else if (left == letters.size() - 1 && letters[left] <= target) {
            return letters[0];
        }
        
        return letters[left];
    }
};
```

## :memo: Search in Rotated Sorted Array (Medium)
![](https://i.imgur.com/S8Ohqd0.png)

### 作法
這一題要先確定mid與left的大小關係，確定從left和mid之間的大小關係，判斷後如果left小於mid代表left到mid遞增，而target介於兩者之間就向左找否則向右找，如果left大於mid代表從mid到right是遞增，而如果target介於mid與right之間就向右找否則向左找
```
class Solution {
public:
    int search(vector<int>& nums, int target) {
        int left = 0;
        int right = nums.size() - 1;
        int mid = 0;
        
        while (left <= right) {
            mid = (left + right) / 2;
            if (nums[mid] == target) {
                return mid;
            }
            else if (nums[mid] >= nums[left]) {
                if (target >= nums[left] && target < nums[mid]) {
                    right = mid - 1;
                }
                else {
                    left = mid + 1;
                }
            }
            else {
                if (target <= nums[right] && target > nums[mid]) {
                    left = mid + 1;
                }
                else {
                    right = mid - 1;
                }
            }
        }
        
        return -1;       
    }
};
```

## :memo: *Find Peak Element (Medium)
![](https://i.imgur.com/Zo7BxO3.png)

### 作法 my code C
這一題我寫得很冗條件太多餘，比較如下
```
int findPeakElement(int* nums, int numsSize){
    int left = 0;
    int right = numsSize - 1;
    int mid = 0;
    
    if (numsSize == 1)
    {
        return 0;
    }
    
    if (numsSize == 2)
    {
        if (nums[0] > nums[1])
        {
            return 0;
        }
        return 1;
    }
    
    while (left < right)
    {
        mid = left + (right - left) / 2;
        //printf("l:%d m:%d r:%d\n",left,mid,right);
        if (mid - 1 >= 0 && mid + 1 <= numsSize - 1)
        {
            if (nums[mid] > nums[mid - 1] && nums[mid] > nums[mid + 1])
            {
                return mid;
            }
            else if (nums[mid] > nums[mid - 1] && nums[mid] < nums[mid + 1])
            {
                left = mid + 1;
            }
            else if (nums[mid] < nums[mid - 1] && nums[mid] > nums[mid + 1])
            {
                right = mid;
            }
            else
            {
                right = mid;
            }
        }
        else
        {
            if (mid == 0 && nums[mid] > nums[mid + 1])
            {
                return mid;
            }
            else if (mid == numsSize - 1 && nums[mid] > nums[mid - 1])
            {
                return mid;
            }
        }
    }

    // if(left!=numsSize)
    // {
    //     // if((left==0 && nums[left]>nums[left+1]) || (left==numsSize-1 && nums[left]>nums[left-1]))
    //     // {
    //     //     return left;
    //     // }
    //     // else if ((nums[left]>nums[left-1] && nums[left]>nums[left+1]))
    //     // {
    //     //     return left;
    //     // }
    //     return left;
    // }
    
    return left;
}
```
### 作法 clean code C
```
int findPeakElement(int* nums, int numsSize){
    int left = 0; 
    int right = numsSize - 1;
    int mid = 0;
    
    while (left < right) 
    {
        mid = (left + right) / 2;
        if (nums[mid] > nums[mid + 1])
        {
            right = mid;
        }
        else
        {
            left = mid + 1;
        }
    }
    
    return left;
}
```

## :memo: *Find K Closest Elements (Medium)
Given a sorted integer array arr, two integers k and x, return the k closest integers to x in the array. 
The result should also be sorted in ascending order.

![](https://i.imgur.com/ZWB0D1z.png)


### 作法 Binary Search To Find The Left Bound 
一開始right=size-k，然後比較x與mid的距離和x與mid+k的距離誰大，x與mid+k的距離較大代表mid+k以後都不會是答案，所以向左找，反之亦然，這一題數線數學很重要

![](https://i.imgur.com/8JY9vKM.png)

![](https://i.imgur.com/dlI6V5M.png)

![](https://i.imgur.com/5nUylSQ.png)

![](https://i.imgur.com/ObPXZzs.png)

```
int left = 0;
int right = arrSize - k;
int mid = 0;

int* ret = (int*) malloc(sizeof(int) * k);
*returnSize = k;

while (left < right)
{
    mid = (left + right) / 2;
    if (x - arr[mid] > arr[mid + k] - x)   //good job!
    {
        left = mid + 1;
    }
    else
    {
        right = mid;
    }
}

for (int i = 0; i < k; i++)
{
    ret[i] = arr[left + i];
}

return ret;
```

## :memo: Search in a Sorted Array of Unknown Size (Medium)
You have a sorted array of unique elements and an unknown size.

![](https://i.imgur.com/vmXClQZ.png)

### 作法
**時間: O(logT) where T is an index of target value.**
```
/**
* // This is the ArrayReader's API interface.
* // You should not implement it, or speculate about its implementation
* class ArrayReader {
*   public:
*     int get(int index);
* };
*/
 
class Solution {
public:
    int search(const ArrayReader& reader, int target) {
        if (reader.get(0) == target) {
            return 0;
        }
        
        // search boundaries 一直乘以2直到超過target
        int left = 0;
        int right = 1;
        while (reader.get(right) < target) {
            left = right;
            right <<= 1 ;
        }
        
        //  binary search
        int pivot;
        int num;
        while (left <= right) {
            pivot = left +((right - left) >> 1);
            num = reader.get(pivot);
            
            if (num == target) {
                return pivot;
            }
            if (num > target) {
                right = pivot - 1;
            }
            else {
                left = pivot + 1;
            }
        }
        
        // there is no target element
        return -1;
    }
};
```

## :memo: *Pow(x, n) (Medium)
Implement pow(x, n), which calculates x raised to the power n (i.e., xn).

![](https://i.imgur.com/LuBfQvL.png)

### 作法 fast power algorithm
**時間: O(logN)**
**空間: O(1)**
```
class Solution {
public:
    double myPow(double x, int n) {
        long long N = n;
        if (N < 0) {
            x = 1 / x;
            N = -N;
        }
        double ans = 1;
        double current_product = x;
        for (long long i = N; i; i /= 2) {
            if (i % 2 == 1) {
                ans = ans * current_product;
            }
            current_product = current_product * current_product;
        }
        
        return ans;
    }
};
```

## :memo: *Find the Duplicate Number (Medium)
![](https://i.imgur.com/dEW0IQQ.png)

### 作法 Cycle Detection
這一題solution有很多作法，此作法比Binary Search快，比Hash Table省空間
**時間: O(N)**
**空間: O(1)**

![](https://i.imgur.com/U3J2tck.png)

![](https://i.imgur.com/LnWWqKO.png)
![](https://i.imgur.com/I9cHtLS.png)
```
int tortoise = nums[0];
int hare = nums[0];

do
{
    tortoise = nums[tortoise];
    hare = nums[nums[hare]];
}while (tortoise != hare);

tortoise = nums[0];

while (tortoise != hare)
{
    tortoise = nums[tortoise];
    hare = nums[hare];
}

return hare;
```

## :memo: Find Minimum in Rotated Sorted Array (Medium)
![](https://i.imgur.com/pkbTIQf.png)

### 作法 my code C
我的作法是先讓size==1,2的獨立處理後判斷mid-1和mid+1有沒有超出範圍，再用mid跟mid-1和mid+1比較然後再跟right比判斷遞增或驟降決定向左找或向右找，比較如下
my code:
```
int findMin(int* nums, int numsSize){
    int left = 0;
    int right = numsSize - 1;
    int mid = 0;
    
    if (numsSize == 1)
    {
        return nums[0];
    }
    
    if (numsSize == 2)
    {
        if (nums[0] < nums[1])
        {
            return nums[0];
        }
        return nums[1];
    }
    
    while (left < right)
    {
        mid = left + (right - left) / 2;
        //printf("l:%d m:%d r:%d\n",left,mid,right);
        if (mid - 1 >= 0 && mid + 1 <= numsSize - 1)
        {   
            if (nums[mid] < nums[mid - 1] && nums[mid] < nums[mid + 1])
            {
                return nums[mid];
            }
            else if (nums[mid] < nums[right])
            {
                right = mid;
            }
            else
            {
                left = mid + 1;
            }
        }
        else
        {
            // if(mid==0 && nums[mid]<nums[mid+1])
            // {
            //     return nums[mid];
            // }
            // else if(mid==numsSize-1 && nums[mid]<nums[mid-1])
            // {
            //     return nums[mid];
            // }
            return nums[mid];
        }
    }
    
    return nums[left];
}
```

### 作法 solution
![](https://i.imgur.com/8vfpr3f.png)

```
int findMin(int* nums, int numsSize){
    if (numsSize == 1) 
    {
        return nums[0];
    }

    // initializing left and right pointers.
    int left = 0;
    int right = numsSize - 1;
    int mid = 0;

    // if the last element is greater than the first element then there is no
    // rotation.
    // e.g. 1 < 2 < 3 < 4 < 5 < 7. Already sorted array.
    // Hence the smallest element is first element. A[0]
    if (nums[right] > nums[0])
    {
        return nums[0];
    }

    // Binary search way
    while (right >= left)
    {
        // Find the mid element
        mid = left + (right - left) / 2;

        // if the mid element is greater than its next element then mid+1 element is the
        // smallest
        // This point would be the point of change. From higher to lower value.
        if (nums[mid] > nums[mid + 1])
        {
            return nums[mid + 1];
        }

        // if the mid element is lesser than its previous element then mid element is
        // the smallest
        if (nums[mid - 1] > nums[mid])
        {
            return nums[mid];
        }

        // if the mid elements value is greater than the 0th element this means
        // the least value is still somewhere to the right as we are still dealing with
        // elements
        // greater than nums[0]
        if (nums[mid] > nums[0])
        {
            left = mid + 1;
        }
        else
        {
            // if nums[0] is greater than the mid value then this means the smallest value
            // is somewhere to
            // the left
            right = mid - 1;
        }
    }
    
    return INT_MAX;
}
```

## :memo: Find Minimum in Rotated Sorted Array II (Hard)
![](https://i.imgur.com/Q4N7AKo.png)
這一題數字會重複

### 作法
![](https://i.imgur.com/PtG0Y7U.png)
![](https://i.imgur.com/n9uta21.png)
![](https://i.imgur.com/xbNRQfn.png)

```
// template #2
int findMin(int* nums, int numsSize){
    int left = 0;
    int right = numsSize - 1;
    int mid = 0;
    
    while (left < right)
    {
        mid = left + (right - left) / 2;
        //printf("l:%d m:%d r:%d\n",left,mid,right);
        if (nums[mid] < nums[right])
        {
            right = mid;
        }
        else if (nums[mid] > nums[right])
        {
            left = mid + 1;
        }
        else
        {
            right--;   //key point!!
        }       
    }
    
    return nums[left];
}
```

## :memo: Median of Two Sorted Arrays (Hard)
![](https://i.imgur.com/ZNP3vq1.png)

### 作法
i和j分別從兩個sorted陣列左邊開始讀，像是MergeSort的做法一樣排序，很標準的寫並記錄數值
```
double findMedianSortedArrays(int* nums1, int nums1Size, int* nums2, int nums2Size){
    int n = nums1Size + nums2Size;
    float nums3[n];
    float median;
    int i,j,k,mid;
    i = j = k = 0;

    while (i < nums1Size && j < nums2Size)
    {
        if (nums1[i] < nums2[j])
        {
            nums3[k++] = nums1[i++];
        }
        else
        {
            nums3[k++] = nums2[j++];
        }
    }
    for (; i < nums1Size; i++)
    {
        nums3[k++] = nums1[i];
    }
    for (; j < nums2Size; j++)
    {
        nums3[k++] = nums2[j];
    }

    if (n % 2 == 0)
    {
        mid = n / 2;
        median = (nums3[mid] + nums3[mid - 1]) / 2;

        return median;
    }
    else
    {
        median = nums3[n / 2];
        return median;
    }

    return;
}
```

## :memo: Find K-th Smallest Pair Distance (Hard)
![](https://i.imgur.com/03QjLTO.png)

### 作法
We will use a sliding window approach to count the number of pairs with distance <= guess.
For every possible right, we maintain the loop invariant: left is the smallest value such that nums[right] - nums[left] <= guess.
Then, the number of pairs with right as it's right-most endpoint is right - left, and we add all of these up.
```

int cmp(const void *a, const void *b)
{
    return *(const int *) a - *(const int *) b;
}

int smallestDistancePair(int* nums, int numsSize, int k){
    qsort(nums, numsSize, sizeof(int), cmp);
    
    int lo = 0;
    int hi = nums[numsSize - 1] - nums[0];   //key point
    int mi = 0;
    
    while (lo < hi)
    {
        mi = (lo + hi) / 2;
        int count = 0;
        int left = 0;
        for(int right = 0; right < numsSize; ++right)
        {
            while (nums[right] - nums[left] > mi)
            {
                left++;
            }
            count += (right - left);
        }
        //count = number of pairs with distance <= mi
        if (count >= k)
        {
            hi = mi;
        }
        else
        {
            lo = mi + 1;
        }
    }
    
    return lo;
}
```

## :memo: Split Array Largest Sum (Hard)
![](https://i.imgur.com/hqN0MfC.png)


### 作法
minimumSubarraysRequired
![](https://i.imgur.com/VmLjNcA.png)

```
int minimumSubarraysRequired(int* nums, int numsSize, int maxSumAllowed){
    int currentSum = 0;
    int splitsRequired = 0;
    
    for (int i = 0; i < numsSize; i++)
    {
        // Add element only if the sum doesn't exceed maxSumAllowed
        if (currentSum+nums[i] <= maxSumAllowed)
        {
            currentSum += nums[i];
        }
        else
        {
            // If the element addition makes sum more than maxSumAllowed
            // Increment the splits required and reset sum
            currentSum = nums[i];
            splitsRequired++;
        }
    }
    
    // Return the number of subarrays, which is the number of splits + 1
    return splitsRequired + 1;
}

int splitArray(int* nums, int numsSize, int m){
    // Find the sum of all elements and the maximum element
    int sum = 0;
    int maxElement = INT_MIN;
    
    for (int i = 0; i < numsSize; i++)
    {
        sum += nums[i];
        maxElement = fmax(maxElement, nums[i]);
    }
    
    // Define the left and right boundary of binary search
    int left = maxElement;
    int right = sum;
    int minimumLargestSplitSum = 0;
    while (left <= right)
    {
        // Find the mid value
        int maxSumAllowed = left + (right - left) / 2;
        
        // Find the minimum splits. If splitsRequired is less than
        // or equal to m move towards left i.e., smaller values
        if (minimumSubarraysRequired(nums, numsSize, maxSumAllowed) <= m)
        {
            right = maxSumAllowed - 1;
            minimumLargestSplitSum = maxSumAllowed;
        }
        else
        {
            // Move towards right if splitsRequired is more than m
            left = maxSumAllowed + 1;
        }
    }
    
    return minimumLargestSplitSum;
}
```

## :memo: Tips
有些題目會隱藏著Binary Search解法，需要思考二分的可能性

條件不會只是單純比mid與target的大小，會變成以mid為基礎做變化的條件式，要去思考如何寫

二元搜尋法方法簡單運用卻靈活，要聰明的談條件，確定100%正確的case當作第一個if，其餘cases都塞到1個else，不用過多的else if包裝，這樣反而導致錯誤

畫圖去trace，case by case

## :memo: 刷題檢討
很多題目寫得太冗，或是想不到數學意義的表示式，最後不知道回傳left還是right，要了解切一半後左右半邊代表的意義，以及有時要逆向思考，看似>其實要向左找，看似<其實要向右找，最重要的是那一個二分的條件(第一個if)要100%正確，
if(){} 
else if(){} 
else{}
的順序也會影響演算法正確性，有可能順序不對就錯了
如:找誰的倍數(3,5,15)
15要先

**想要提高演算法的速率，直覺可以想到二元搜尋!!**