Review of binary search algorithm

Posted by edwinlcy on Fri, 11 Feb 2022 13:11:19 +0100

Basic review

Firstly, the binary search algorithm is based on the current array as an ordered array;
In fact, it is the value of finding the center point every time; If the target number is equal to the center point, it can be returned directly. If the target number is less than the middle number, it will be found on the left of the center point, otherwise it will be found on the right of the center point

In practice, binary search can be realized by different templates, so the specific rules for judging the center point are different

Starting from the classic basic problems; Force deduction original question 704. Binary search;

class Solution {
    public int search(int[] nums, int target) {
       //Basic binary search template;
       int n = nums.length;
       if(n < 2) return nums[0] == target ?0:-1;

       int left  = 0;
       int right = n - 1;
       while(left<=right){
           //Calculate the midpoint;
           //int middle = (right + left)/2; This can be used when overflow is not considered;
           int middle = left + (right-left)/2;
           if(nums[middle] == target){
               return middle;
           }else if(nums[middle] > target){
               right = middle -1;
           }else{
               left = middle +1;
           }
       }
       return -1; 
    }
}

You can also write this after slightly changing the conditions

class Solution {
    public int search(int[] nums, int target) {
       //Basic template search;
       int n = nums.length;
       if(n < 2) return nums[0] == target ?0:-1;

       int left  = 0;
       int right = n - 1;
       while(left<right){
           //Calculate the midpoint;
           //int middle = (right + left)/2; This can be used when overflow is not considered;
           int middle = left + (right-left)/2;
           if(nums[middle] >= target){
               right = middle;
           }else{
               left = middle +1;
           }
       }
       //Judge whether it exists;
       if(nums[left] == target){
           return left;
       }
       return -1; 
    }
}

Note that if the boundary condition is left = middle; Time;
Then the calculation center point needs to be rounded up (+ 1);
This is because when there are only two numbers in the array interval, the center point of the calculation is always the left node, so the loop cannot be exited;

class Solution {
    public int search(int[] nums, int target) {
       //Basic binary search template;
       int n = nums.length;
       if(n < 2) return nums[0] == target ?0:-1;

       int left  = 0;
       int right = n - 1;
       while(left<right){
           //Calculate the midpoint;
           //int middle = (right + left + 1)/2; This can be used when overflow is not considered;
           int middle = left + (right-left)/2 +1;
           if(nums[middle] > target){
               right = middle - 1;
           }else{
               left = middle;
           }
       }
       //Judge whether it exists;
       if(nums[left] == target){
           return left;
       }
       return -1; 
    }
}

Or change the judgment condition of the cycle to left + 1 < right
Since two elements will be left in the end, it should be noted that two judgments should be made after exiting the loop;

class Solution {
    public int search(int[] nums, int target) {
       //Basic binary search template;
       int n = nums.length;
       if(n < 2) return nums[0] == target ?0:-1;

       int left  = 0;
       int right = n - 1;
       while(left+1<right){
           //Calculate the midpoint;
           int middle = left + (right-left)/2;
           if(nums[middle] == target){
               return middle;
           }else if(nums[middle] < target){
               left = middle;
           }else{
               right = middle;
           }
       }
       //Judge whether it exists;
       if(nums[left] == target){
           return left;
       }
       if(nums[right] == target){
           return right;
       }

       return -1; 
    }
}

practice

Force buckle 35 questions - Search insertion position

Original position: 35. Search insertion position

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.
Please use a time complexity of O(log n) Algorithm.

Example 1:
input: nums = [1,3,5,6], target = 5
 output: 2
    
Example 2:
input: nums = [1,3,5,6], target = 2
 output: 1
    
Example 3:
input: nums = [1,3,5,6], target = 7
 output: 4
    
Example 4:
input: nums = [1,3,5,6], target = 0
 output: 0
    
Example 5:
input: nums = [1], target = 0
 output: 0

Tips:
1 <= nums.length <= 104
-104 <= nums[i] <= 104
nums Arranges the array in ascending order without repeating elements
-104 <= target <= 104

(1) Double pointer

class Solution {
    public int searchInsert(int[] nums, int target) {
        int n = nums.length;
        //Since this is an array that has been sorted, you can directly tail insert;
        if(nums[n-1] < target) return n;

        //Simple double pointer;
        int left = 0;
        int right = n-1;
        while(left < right){
            int middle = left + (right-left)/2;
            if(nums[middle] < target){
                left = middle +1;
            }else{
                right = middle;
            }
        }
        //Because it can be inserted absolutely, it will return to this position directly;
        return left;
    }
}

(2) Double pointer idea written before

class Solution {
    public int searchInsert(int[] nums, int target) {
        if(nums.length == 0) return 0;
        List<Integer> list = new ArrayList<>();
        for(int i:nums){
            list.add(i);
        }

        //Binary search type questions; First define the left and right pointers;
        int left = 0;
        int right = nums.length-1;
        //Center value;
        int middle = 0;
        while(left<=right){
            middle = left + (right - left)/2;
            if(nums[middle] == target)
                  return middle;

            //If the center point is greater than the target value, search on the left;      
            if(nums[middle] > target){
                right = middle - 1;
            }
            //If it is less than the target value, search on the right;
            if(nums[middle] < target){
                left = middle+1;
            }
        }
        //If not found, return to the new addition location;
        return right+1;
    }
}

(3) Use auxiliary variable record

class Solution {
    public int searchInsert(int[] nums, int target) {
        int n = nums.length;
        //Since this is an array that has been sorted, you can directly tail insert;
        if(nums[n-1] < target) return n;

        //Simple double pointer;
        int left = 0;
        int right = n-1;
        //Auxiliary variables;
        int res = 0;
        while(left <= right){
            int middle = left + (right-left)/2;
            if(nums[middle] >= target){
                 res = middle;
                 right = middle -1;
            }else{
                 left = middle +1;
            }
        }
        return res;
    }
}

Force deduction question 34: find the first and last positions of elements in the sorted array

Original position: 34. Find the first and last position of an element in a sorted array
Title Requirements

Give an array of integers in ascending order nums,And a target value target. 
Find the start and end positions of the given target value in the array.
If the target value does not exist in the array target,return [-1, -1]. 
Advanced:
You can design and implement a time complexity of O(log n) Does our algorithm solve this problem?
 
Example 1:
Input: nums = [5,7,7,8,8,10], target = 8
 Output:[3,4]

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

Example 3:
Input: nums = [], target = 0
 Output:[-1,-1]
 
Tips:
0 <= nums.length <= 105
-109 <= nums[i] <= 109
nums Is a non decreasing group
-109 <= target <= 109

Use two binary searches;
Note that when the cycle judgment condition is left < right;
The first and last position of this question;
When the location query appears for the first time, focus on the left side of the query,
But the last time a location query occurs, focus on the right side of the query;
Also note that the boundary mentioned earlier is left = middle; The loop exit problem of needs to be rounded up;

class Solution {
    public int[] searchRange(int[] nums, int target) {
        int[] res = new int[2];
        int n = nums.length;
        //Exclude special circumstances;
        if(n == 0) 
           return new int[]{-1,-1};

        //Use the binary search method. Note that it needs to be used twice;
        int start = binarySearchStart(nums,target);
        int end   = binarySearchEnd(nums,target);
        res[0] = start;
        res[1] = end;
        return  res;
    }

    //Find the location of the element that appears for the first time;
    private int binarySearchStart(int[] nums,int target){
        int left  = 0;
        int right = nums.length-1;
        while(left < right ){
            //Center point;
            int middle = left + (right-left)/2;
            if(target > nums[middle]){
                //right;
                left = middle +1;
            }else{
                //target <= nums[middle]
                //Left side exists;
                right = middle;
            }
        }
        //Since the existence cannot be determined, it needs to be determined here;
        if(nums[left] == target){
            return left;
        } 
        return -1;
    }

    //Find the location of the last occurrence;
    private int binarySearchEnd(int[] nums,int target){
        int left  =0;
        int right =nums.length-1;
        while(left < right){
            //The central point here mainly focuses on preventing the problem of dead circulation, which needs to be rounded up;
            int middle = left + (right - left )/2 +1;
            if(target < nums[middle]){
                //left;
                right = middle - 1;
            }else{
                //target >= nums[middle];
                //Right side exists;
                left = middle;
            }
        }
        //Since the existence cannot be determined, it needs to be determined here;
        if(nums[left] == target){
            return left;
        } 
        return -1;
    }
}

Question 744: find the smallest letter larger than the target letter

Original position: 744. Find the smallest letter larger than the target letter

Give you a sorted list of characters letters ,The list contains only lowercase English letters.
Give another target letter target,Please look for the smallest letter in this sequence that is larger than the target letter.
In comparison, letters appear in a circular sequence. for instance:
If the target letter target = 'z' And the character list is letters = ['a', 'b'],Then the answer returns 'a'
 

Example:

input:
letters = ["c", "f", "j"]
target = "a"
output: "c"

input:
letters = ["c", "f", "j"]
target = "c"
output: "f"

input:
letters = ["c", "f", "j"]
target = "d"
output: "f"

input:
letters = ["c", "f", "j"]
target = "g"
output: "j"

input:
letters = ["c", "f", "j"]
target = "j"
output: "c"

input:
letters = ["c", "f", "j"]
target = "k"
output: "c"
 

Tips:
letters The length range is[2, 10000]In the interval.
letters It consists only of lowercase letters and contains at least two different letters.
Target letter target Is a lowercase letter.

Use binary search;
Note that if the target letter found is not in the array, the first letter of the array is returned;

class Solution {
    public char nextGreatestLetter(char[] letters, char target) {
        int n = letters.length;
        //If there is no or the last digit, return the first letter directly;
        if(letters[n-1] <= target){
            return letters[0];
        }

        //Standard binary search;
        int left = 0;
        int right = n-1;
        while(left < right){
            int middle = left + (right - left) /2;
            if(target >= letters[middle]){
                left = middle+1;
            }else{
                right = middle;
            }
        }
        return letters[left];
    }
}

Force deduction question 275: H index II

Original position: 275. H index II
Title Description:

Give you an array of integers citations ,
among citations[i] Indicates the researcher's second opinion i The number of citations of this paper,
citations Has been arranged in ascending order.
Calculate and return the researcher's h Index.

h Definition of index: h Stands for "high references"( high citations),
Of a scientific researcher h Index refers to his / her( n (in a paper)
Total h Papers were cited at least h Times.
And the rest n - h Each paper is cited no more than h Times.

Tip: if h There are many possible values, h The index is the largest one.
Please design and implement an algorithm with logarithmic time complexity to solve this problem.
 
Example 1:
Input: citations = [0,1,3,5,6]
Output: 3 
Explanation:
Given an array, the researcher has a total of 5 papers,
Each paper is cited accordingly 0, 1, 3, 5, 6 Times.
Since the researchers have three papers, each of which has been cited at least three times,
The other two papers were cited no more than three times, so her h The index is 3.
     
Example 2:
Input: citations = [1,2,100]
Output: 2
 
Tips:
n == citations.length
1 <= n <= 105
0 <= citations[i] <= 1000
citations Sort in ascending order

H index = res, the number of papers cited at least uses the current res;
In fact, the paper is regarded as an interval; Find the starting position of papers with high quotation;
Finally, the number of papers with high citation is calculated;

class Solution {
    public int hIndex(int[] citations) {
        int n = citations.length;
        //Special judgment;
        if(citations[n-1]==0){
            return 0;
        }
        //Use binary search;
        int left  = 0;
        int right = n-1;
        while(left<right){
            //Calculation center point,
            //In fact, the center point here is the least position of the high reference area;
            int middle = left + (right-left)/2;
            //n-middle: number of highly cited articles;
            if((n-middle) > citations[middle]){
                left = middle+1;
            }else{
                right = middle;
            }
        }
        //Final calculated H number;
        return n-left;
    }
}

Question 436: finding the right interval

Original position: 436. Find the right interval
Title Description

Give you an interval array intervals ,
among intervals[i] = [starti, endi] ,And each starti All different.

section i The right section of can be recorded as the section j ,And meet startj >= endi ,And startj Minimize.

Returns an interval by each i An array consisting of the minimum starting position of the right section of.
If a certain interval i If there is no corresponding right section, the subscript i The value at is set to -1 . 

Example 1:
Input: intervals = [[1,2]]
Output:[-1]
Explanation: there is only one interval in the set, so the output-1. 

Example 2:
Input: intervals = [[3,4],[2,3],[1,2]]
Output:[-1,0,1]
Explanation: for [3,4] ,There is no "right" section that meets the conditions.
about [2,3] ,section[3,4]Has the smallest "right" starting point;
about [1,2] ,section[2,3]Has the smallest "right" starting point.

Example 3:
Input: intervals = [[1,4],[2,3],[3,4]]
Output:[-1,2,-1]
Explanation: for interval [1,4] and [3,4] ,There is no "right" section that meets the conditions.
about [2,3] ,section [3,4] There is the smallest "right" starting point.
 
Tips:
1 <= intervals.length <= 2 * 104
intervals[i].length == 2
-106 <= starti <= endi <= 106
 The starting point of each interval is different
class Solution {
    public int[] findRightInterval(int[][] intervals) {
          int n = intervals.length;
          //Exclude an array;
          if(n < 2) return new int[]{-1};
          int nums[] = new int[n];
          int res[]  = new int[n];
          //Here, the map stores the value of the left breakpoint between cells. The title indicates that the left end point will not be repeated;
          HashMap<Integer,Integer> map = new HashMap<>();
          for(int i =0;i<n;i++){
              map.put(intervals[i][0],i);
              nums[i] = intervals[i][0];
          }

          //Sort the current left endpoint interval;
          Arrays.sort(nums);
          for(int i =0;i<n;i++){
              //Find out whether there is an interval position matching the left endpoint interval;
              int good = binarySearch(nums,intervals[i][1]);
              if(good == -1){
                  res[i] = -1;
              }else{
                  //Find the value corresponding to the left endpoint position;
                  res[i] = map.get(nums[good]);
              }
          }
          return res;
    }

    //Binary search;
    private int binarySearch(int[] nums,int target){
        int n = nums.length;
        int left = 0;
        int right = n-1;
        //Refuse directly under special circumstances;
        if(nums[n-1] < target){
            return -1;
        }

        while(left < right){
            //Calculation center point;
            int middle = left+(right-left) /2;
            if(nums[middle] <  target){
                left = middle +1;
            }else{
                right = middle;
            }
        }
        return left;
    }
}

Question 611: number of effective triangles

Original position: 611. Number of effective triangles
Title Description:

Given an array containing nonnegative integers nums ,Returns the number of triples in which three sides of a triangle can be formed.
 
Example 1:
input: nums = [2,2,3,4]
output: 3
 explain:The effective combination is: 
2,3,4 (Use first 2)
2,3,4 (Use the second 2)
2,2,3
    
Example 2:
input: nums = [4,2,3,4]
output: 4
 
Tips:
1 <= nums.length <= 1000
0 <= nums[i] <= 1000

In the case of a < = B < = C, the sum of any two sides is greater than the third side;

Find the first C that meets the conditions; That is, the length of the interval [B + 1, c);

class Solution {
    public int triangleNumber(int[] nums) {
        int n = nums.length;
        //It does not meet the three sides and does not meet the meaning of the topic;
        if(n<3){
            return 0;
        }
        //To use binary search, first sort the current array;
        //If sorting is not mandatory, API functions are not used, and the sorting method of array tool class can be used;
        Arrays.sort(nums);
        int res = 0;

        for(int a = 0;a<n-2;a++){
            for(int b = a+1;b<n-1;b++){
                //Binary search;
                int left  = b+1;
                int right = n;
                while(left < right){
                    int middle = left + (right - left)/2;
                    //Here, the sum of the first two edges can be regarded as the target value;
                    if( (nums[a]+nums[b]) > nums[middle]){
                        left = middle +1;
                    }else{
                        //(nums[a]+nums[b]) <= nums[middle];
                        right = middle;
                    }
                }
                //Calculated length; In fact, it is [b+1 ~ the position of the third side consistent] = = [b+1 ~ left];
                res += (left - b -1);
            }
        }
        return res;
    }
}

Question 658: find K closest elements

Original address: 658. Find the K closest elements

Given a sorted array arr ,Two integers k and x ,
Find the closest from the array x(The difference between the two numbers is the smallest k number. The returned results must be arranged in ascending order

integer a Ratio integer b Closer x Need to meet:
|a - x| < |b - x| perhaps
|a - x| == |b - x| And a < b
 
Example 1:
Input: arr = [1,2,3,4,5], k = 4, x = 3
 Output:[1,2,3,4]

Example 2:
Input: arr = [1,2,3,4,5], k = 4, x = -1
 Output:[1,2,3,4]
 

Tips:
1 <= k <= arr.length
1 <= arr.length <= 104
arr Sort in ascending order
-104 <= arr[i], x <= 104

Because it is necessary to return elements close to the integer x;
When x is less than the first element of the array, the first k elements of the array can be returned;
When x is greater than the last element of the array, return the penultimate k elements of the array;
In other cases, use dichotomy to find k elements close to x;

Consider the way of using interval; Here, the length of the interval is k;

  • x is not in the array range,
    If x is on the left side of the array range, delete the elements on the right one by one;
    If x is on the right side of the array range, delete the elements on the left one by one;
  • x is in the array range;
    When x is in the left or middle of the array, the elements on the right are deleted;
    When x is to the right of the array Delete the element on the left;
class Solution {
    public List<Integer> findClosestElements(int[] arr, int k, int x) {
        List<Integer> list = new ArrayList<>();
        int n = arr.length;
        //Use binary search method; Pay attention to the initial range of the right section;
        int left  = 0;
        int right = n-k; 
        while(left < right){
            int middle = left + (right - left) /2;
            //Note that there will be two situations;
            //1. When the value of x is not within the range of interval: x is on the right of the interval;
            //2. When the value of x is within the range of the interval: x is in the right position of the interval;
            if(x-arr[middle] > arr[middle+k]-x){
                //Keep the right side;
                left = middle +1;
            }else{
                //When the value of x is within the range of the interval: x is at the left position of the interval,
                //When the value of x is within the range of the interval: x is in the middle of the interval,
                //Keep the left side;
                right = middle;
            }
        }


        //The final left position is the starting point of the result interval;
        for(int i = left;i<left+k;i++){
            list.add(arr[i]);
        }
        return list;
    }
}

Force deduction 4: find the median of two positive arrays

Original position: 4. Find the median of two positive arrays
hard

Given two sizes are m and n Positive order (from small to large) array of nums1 and nums2. 
Please find and return the median of these two positive arrays.
The time complexity of the algorithm should be O(log (m+n)) . 

Example 1:
Input: nums1 = [1,3], nums2 = [2]
Output: 2.00000
 Explanation: merge arrays = [1,2,3] ,Median 2
    
Example 2:
Input: nums1 = [1,2], nums2 = [3,4]
Output: 2.50000
 Explanation: merge arrays = [1,2,3,4] ,median (2 + 3) / 2 = 2.5
 
Tips:
nums1.length == m
nums2.length == n
0 <= m <= 1000
0 <= n <= 1000
1 <= m + n <= 2000
-106 <= nums1[i], nums2[i] <= 106

Previous merging and sorting practices

class Solution {
    public double findMedianSortedArrays(int[] nums1, int[] nums2) {
        //The length of two arrays;
        int m = nums1.length;
        int n = nums2.length;
        if(m==0 && n == 0)
         return 0;
        //Merged array;
        int[] total = new int[m+n];
        //The length of the merged array;
        int tLen = m+n;
        //The pointer to the first array, the pointer to the second array, and the pointer to the merged array;
        int i = 0, j =0,k=0;
        if(m!=0&& n!=0){
        while(i<m || j<n){
            //If the of array 1 is relatively small, put the of array 1 in first;
            if(nums1[i] <= nums2[j]){
               total[k++] = nums1[i++];
            }else{
                //Otherwise, put the of array 2;
                total[k++] = nums2[j++];
            }
            if(i==m || j==n) break;
         }
       }
        //When an array is empty, it will continue to be added to the merged array;
        while(i<m){
            total[k++] = nums1[i++];
        }
        //When the other array is empty, do the same;
        while(j<n){
            total[k++] = nums2[j++];
        }
        //Calculate the median;
        int min = tLen/2;
        //Even length;
        if(tLen%2==0){
        return (double)(total[min]+total[min-1])/2;
        }
        else{
            return (double)(total[min]);
        } 
    }
}