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
- Arrays must be ordered when using binary lookups
- 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
-
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
-
-
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
-
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 } };
-
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)