Binary search summary

Posted by mrdance on Thu, 27 Jan 2022 05:26:10 +0100

Binary search

Binary search is a very basic algorithm. Its principle is very simple, but it is easy to make mistakes when writing code, especially when defining the range of intervals. Recent brush Code Capriccio , I have a more comprehensive and detailed understanding of binary search and write my own summary. Thank you very much for the help of code Capriccio to my vegetable chicken.

prerequisite

  1. Arrays must be ordered when using binary lookups
  2. There are no duplicate elements in the array, otherwise the returned subscripts may be inconsistent

Boundary conditions (interval definition)

There are two kinds of boundary definitions for binary search. If you don't understand them thoroughly, it's easy to write wrong code

Boundary processing shall be carried out according to the definition of search interval

Different boundary definitions directly affect the update of cyclic conditions and right

  1. Left closed and right closed, i.e. [left, right], that is, the right subscript is included in the array we want to find

    • At this time, the cycle condition is while (left < = right), because left == right, that is, [left, left] is meaningful

    • When updating right, right = mid - 1. Because the subscript of mid has been searched, the range of [left, mid - 1] should be searched

  2. The right subscript is not included in the array we are looking for

    • At this time, the cycle condition is while (left < right), because left == right, that is, [left, left) is meaningless

    • When updating right, right = mid, because you want to find the range of [left, mid], which does not include the mid subscript

When left is updated, it is left = mid + 1

Prevent overflow

When we look at a lot of codes on the Internet to calculate mid

Not mid = (left + right) / 2,

Instead, mid = left + (right - left) / 2

The two are equivalent, but the former is easy to overflow when the left and right values are large, while the latter will not

leetcode related topics

704. Binary search

Problem Description: given an n-element ordered (ascending) integer array num and a target value target, write a function to search the target in num. if the target value exists, return the subscript, otherwise return - 1.

Example 1:

Input: num = [- 1,0,3,5,9,12], target = 9
Output: 4
Explanation: 9 appears in nums with subscript 4
Example 2:

Input: num = [- 1,0,3,5,9,12], target = 2
Output: - 1
Explanation: 2 does not exist in nums, so - 1 is returned

Tips:

You can assume that all elements in nums are not repeated.
n will be between [1, 10000].
Each element of num will be between [- 9999, 9999].

This is a basic problem of binary search. Two writing methods are given. The following questions only give the writing method of left closing and right closing by default

  1. Left closed right closed writing

    class Solution {
    public:
        //Left closed right closed writing
        int search(vector<int>& nums, int target) {
            int left = 0, right = nums.size() - 1;  //Left closed right closed interval
            while (left <= right) {                 //The condition is<=
                int mid = left + (right-left)/2;    //Prevent overflow
                if (nums[mid] == target) {          //Find direct return subscript
                    return mid;
                } else if (nums[mid] > target) {    //At this time, the update interval is on the left
                    right = mid - 1;
                }
                else {                              //At this time, the update interval is on the right
                    left = mid + 1;
                }
            }
            return -1;                              //Return - 1 not found
        }
    };
    
  2. Left closed right open writing

    class Solution {
    public:
        // Left closed right open writing
        int search(vector<int>& nums, int target) {
            int left = 0, right = nums.size();   //Difference 1: left closed right open section
            while (left < right) {               //Difference 2: judgment conditions        
                int mid = left + (right-left)/2;    
                if (nums[mid] == target) {          
                    return mid;
                } else if (nums[mid] > target) {    
                    right = mid;                //Difference 3: right processing
                }
                else {                              
                    left = mid + 1;
                }
            }
            return -1;                              
        }
    };
    

69. Sqrt(x)

Give you a nonnegative integer x, calculate and return the arithmetic square root of X.

Since the return type is an integer, only the integer part will be retained and the decimal part will be rounded off.

Note: it is not allowed to use any built-in exponential functions and operators, such as pow(x, 0.5) or x ** 0.5.

Example 1:

Input: x = 4
Output: 2
Example 2:

Input: x = 8
Output: 2
Explanation: the arithmetic square root of 8 is 2.82842, Since the return type is an integer, the decimal part will be rounded off.

Tips:

0 <= x <= 231 - 1

This topic stipulates that exponential functions and operators cannot be used. We think that by traversing the square of integers, integers are ordered and can be searched in two

This topic to write no problem code, we should pay attention to these points

  • left needs to be initialized to 1 instead of 0, or special handling input is 0 or 1
  • mid * mid == x will overflow. Use mid == x / mid (both mid and target are integers)
  • If mid ==x / mid is found, mid is returned. If it is not found, the integer with the largest square less than x is returned, i.e. right.
class Solution {
public:
    int mySqrt(int x) {
        // left must start from 1. The reason is that when x=1 is entered, the calculated mid=0, and an error will be reported when calculating x/mid.
	    // In addition, when the input is greater than or equal to 1, it does not affect the result calculation. When the input is 0, the output is right(0), so it is also reasonable.
        int left = 1, right = x;
        //Left closed right closed interval
        while (left <= right) {
            int mid = left + (right - left) / 2; 
            if (mid > x / mid) {
                right = mid - 1;
            }
            else if (mid < x/mid) {
                left = mid + 1;
            }
            else if (mid == x/mid) {
                return mid;
            }
        }
        // If it is not found, it returns the largest value whose average value is less than x
        return right;
    }
};

367. Effective complete square

Given a positive integer num, write a function. If num is a complete square number, it returns true, otherwise it returns false.

Advanced: do not use any built-in library functions, such as sqrt.

**

Example 1:

Input: num = 16
Output: true
Example 2:

Input: num = 14
Output: false

Tips:

1 <= num <= 2^31 - 1

This question is similar to the previous one, except to judge whether the remainder is zero

class Solution {
public:
    bool isPerfectSquare(int x) {
        int left = 1,right = x;
        while (left <= right) {
            int mid = left + (right - left)/2;
            if (mid == x/mid) {
                if (x % mid == 0) {				//Divide or not
                    return true;
                } 
                return false;
            } else if (mid > x/mid) {
                right = mid - 1;
            } else if (mid < x/mid) {
                left = mid + 1;
            }
        }
        return false;
    }
};

35. Search insertion position

Problem Description: given a sort array and a target value, find the target value in the array and return its index. If the target value does not exist in the array, return the position where it will be inserted in order.

Example 1:

*Input: num = [1,3,5,6], target = 5
Output: 2

Example 2:

Input: num = [1,3,5,6], target = 2
Output: 1

Example 3:

Input: num = [1,3,5,6], target = 7
Output: 4

Example 4:

Input: num = [1,3,5,6], target = 0
Output: 0

Example 5:

Input: num = [1], target = 0
Output: 0

The difference between this question and the previous one is that if the insertion position is not found, instead of - 1 is returned.

Didn't find what to return in the end? We consider the following situations

  • The searched elements are smaller than all the elements in the array. Finally, left = 0 and right = -1. Our door returns 0
  • The searched elements are larger than all the elements in the array. Finally, left = n + 1 (n is the number of elements), right = n
  • The size of the searched element is at the array area level, and the last left = right + 1

So return to left

class Solution {
public:
    //Left closed right closed interval
    int searchInsert(vector<int>& nums, int target) {
       int left = 0, right = nums.size() - 1;
       while (left <= right) {
           int mid = left + (right-left)/2;
           if (nums[mid] == target) {
               return mid;
           } else if (nums[mid] > target) {
               right = mid - 1;
           } else {
               left = mid + 1;
           }
       }
       // left = right + 1 at end
       return left;
    }
};

34. Find the first and last position of an element in a sorted array

Given an integer array nums arranged in ascending order and a target value target. Find the start and end positions of the given target value in the array.

If the target value target does not exist in the array, return [- 1, - 1].

Advanced:

Can you design and implement an algorithm with time complexity of O(log n) to solve this problem?

Example 1:

Input: num = [5,7,7,8,8,10], target = 8
Output: [3,4]
Example 2:

Input: num = [5,7,7,8,8,10], target = 6
Output: [- 1, - 1]
Example 3:

Input: num = [], target = 0
Output: [- 1, - 1]

Tips:

0 <= nums.length <= 105
-109 <= nums[i] <= 109
nums is a non decreasing group
-109 <= target <= 109

  • First find the target value through binary search, and then traverse back and forth to find the start position and end position. The time complexity is nlog(n)

    class Solution {
    public:
        vector<int> searchRange(vector<int>& nums, int target) {
            int right = nums.size() - 1;
            vector<int> res(2, -1);  //Initialization start and end positions, - 1 is not found
            int left = 0;
            //Left closed right closed interval
            while (left <= right) {
                int mid = left + (right - left)/2;
                if (nums[mid] == target) {   	
                    int i = mid, j = mid;
                    while (i >= 0 && nums[i] == target) {//After finding the target value, loop through to find the starting position
                        i--;
                    }
                    while (j < nums.size() && nums[j] == target) {//Loop traversal to find the end position
                        j++;
                    }
                    res[0] = i + 1;
                    res[1] = j - 1;
    
                    return res;
                }
                else if (nums[mid] > target) {
                    right = mid - 1;
                }
                else {
                    left = mid + 1;
                }
            }
            return res;
        }
    };
    
  • Directly find the start position and end position through binary search, and the time complexity is log(n)