Binary search learning summary experience

Binary search is generally used in the search in an ordered array, and it is generally divided into open interval, closed interval and half-open and half-closed interval.

closed interval

A closed interval refers to an interval containing elements on the left and right sides, for example, an interval [a, b]containing the element ab is called a closed interval.

code 1

# 左闭右闭 区间写法  在数组  nums 中寻找 target
def left_close_right_close(nums: List[int], target: int) -> int:
    left = 0
    right = len(nums) - 1
    # 闭区间  [left, right]
    while left < right:
        mid = (left + right) // 2
        if nums[mid] < target:
            # 更新闭区间  [mid+1, right]
            left = mid + 1
        else:
            # [left, mid-1]
            right = mid - 1
    print('left is {} ans num is {}, right is {} and nums is {}'.format(left, nums[left], right, nums[right]))
    return left

open interval

An open interval refers to an interval that does not contain elements on the left and right sides. For example, an interval that (a, b)does not contain the element ab is called a closed interval.

code 2

# 左开右开区间写法
def left_open_right_open(nums: List[int], target: int) -> int:
    # 左开区间,所以 是 -1
    left = -1
    # 因为是右开区间
    right = len(nums)
    # 左开右开区间  [left, right)
    while left + 1 < right:
        mid = (left + right) // 2
        print("mid is {}".format(mid))
        if nums[mid] < target:
            # 更新左开区间  (mid, right)
            left = mid
        else:
            # 更新右开区间  (left, mid)
            right = mid
    print('left index is {}, right index is {}'.format(left, right))
    return right

half open half closed interval

In addition to closed intervals and open intervals, there are also half-open and half-closed intervals


In addition to figuring out that there are several ways to write intervals, let's illustrate with examples.

Category Discussion

  1. Find the target element in an ordered non-repeating array
  2. Find the target element in an ordered array with repeated elements

no repeating elements

Left closed right closed interval

  1. Example data:arr = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]
    1. Find the element at the non-edge position of the array, take finding element 12 as an example
    2. As can be seen from " Code 1 ", the subscript of the finally returned array isleft
    3. The result of the operation is as follows
    4. image.png
  2. Illustrated why it ends up returningleft
    1. image.png
    2. It can be seen from the above figure that to find the final target element, when it is found, it needs to return left, and if the judgment condition is true, then left < rightthe return rightis the same
  3. Detailed commented version of the code
#   二分查找左闭右闭区间
def left_close_right_close(nums: List[int], target: int) -> int:
    # 因为是左闭合区间,所以左边的开始起点为 left = 0 因为 0 是数组的第一个下标位置
    # 如果是左开区间,那么初始化左边起点为 -1
    left = 0
    # 同理,初始化右边起点为 right = len(nums) - 1
    # 如果是 右开区间,那么 right 起点为  len(nums)
    right = len(nums)
    # 这里如果是 小于等于 的话,最终 left = right + 1 将会重合 (target在数组中存在的情况下)
    while left < right:
        mid = (left + right) // 2
        if nums[mid] > target:
            # 更新区间 [left, right = mid - 1]
            right = mid - 1
        else:
            # 更新区间 [left = mid + 1, right]
            left = mid + 1
    # return left or right 因为 判断条件是 left < right 所以 最终跳出循环 left = right
    return left
  1. If the element you are looking for does not exist in the array
    1. **If the searched element is greater than the maximum value in the array, the result is returned****left = 数组长度**
    2. If the searched element is less than the minimum value in the array, the result is returned**left = 0**
    3. Summary: The left-closed right-closed interval is suitable for finding elements whose target elements are not at the boundary of the array, which should be noted

left open right open interval

  1. directly on the code
# 左开右开区间写法
def left_open_right_open(nums: List[int], target: int) -> int:
    # 左开区间,所以 是 -1
    left = -1
    # 因为是右开区间
    right = len(nums)
    # 左开右开区间  [left, right)
    while left + 1 < right:
        mid = (left + right) // 2
        print("mid is {}".format(mid))
        if nums[mid] < target:
            # 更新左开区间  (mid, right)
            left = mid
        else:
            # 更新右开区间  (left, mid)
            right = mid
    print('left index is {}, right index is {}'.format(left, right))
    return right
  1. Differences from left-closed-right-closed writing
    1. The first is to judge the condition left+1 < right, then when the final returnleft+1 = right
    2. The update interval does not need to be added or subtracted, just update the left or right directly
    3. The final returned element result is rightbecause the judgment condition isleft + 1 < right
    4. If the element you are looking for does not exist, the final right = 数组长度+1orright = 0
    5. What needs special attention is that the first element of the search array and the element smaller than the minimum value in the array return the same result
    6. What needs special attention is that the first element of the search array and the element smaller than the minimum value in the array return the same result
    7. What needs special attention is that the first element of the search array and the element smaller than the minimum value in the array return the same result

Left open right closed interval

  1. directly on the code
# 左开右闭 区间写法
def left_open_right_close(nums: List[int], target: int) -> int:
    left = -1
    # 因为是右闭区间
    right = len(nums) - 1
    #   左开右闭   (left, right]
    while left < right:
        # mid = (left + right + 1) // 2
        mid = left + (right + 1 - left) // 2
        print("mid is {}".format(mid))
        if nums[mid] < target:
            # 更新开区间  (mid, right)
            left = mid
        else:
            # 更新闭区间  (left, mid - 1]
            right = mid - 1
    print('left index is {}, right index is {}'.format(left, right))
    return right + 1
  1. look for differences
    1. Left open right close initializationleft = -1, right = len(nums) - 1
    2. It is different when calculating midmid = left + (right + 1 - left) // 2
      1. Note here that the above notation is equivalent tomid = (right + 1 - left) // 2
      2. Why do it- right + 1 - left>Because the right side is a closed interval, it follows the writing method of left-open right-open interval, so carry outright + 1
    3. Since the judgment condition is left < right, it finally returnsleft = right
    4. The final return result isleft + 1 or right + 1
    5. Also pay attention to the case of finding the minimum value in the array

Left closed right open interval

  1. directly on the code
# 左闭右开区间
def left_close_right_open(nums: List[int], target: int) -> int:
    left = 0
    right = len(nums)
    while left < right:
        mid = left + (right - 1 - left) // 2
        print('the mid is {}'.format(mid))
        if nums[mid] > target:
            # 右开区间,直接更新右开区间
            right = mid
        else:
            # 左闭区间,更新左区间 +1
            left = mid + 1
    print('final the left is {},the right is {}'.format(left, right))
    return right - 1
  1. look for differences
    1. Left closed right open initializationleft = 0, right = len(nums)
    2. Calculate mid mid = left + (right - 1 - left)minus 1 calculation
    3. The final result left = rightreturns left - 1orright - 1
    4. Pay attention to the case of finding boundary elements

Contains repeating elements

  1. Example data:arr1 = [1,2,3,4,4,4,4,5] arr2 = [1,2,2,2,2,2,3,4,5]
  2. For the case of repeated elements, it is generally necessary to find the position of its first or last occurrence.

Open left, open right and close left, close right

  1. Test the operation effect of left open right open and left closed right closed writing
    1. Left closed right closed did not find the correct element
    2. image.png
    3. Open left and open right to find the correct element, but there is no way to correctly find the right border of the repeated element
    4. image.png

Find left and right borders

Open left and close right to find the left boundary

  1. The test results go directly to
  2. image.png
  3. Why?
    1. Because the search element of the left-open and right-closed interval finally returns the result is right + 1offset to the right, and finally obtains the left boundary

Close left and open right to find the right boundary

  1. In the same way, the left-closed right-open interval finds the right boundary

Thousands of cases return to one, summary of details

  1. After learning about the various ways of writing
  2. Can flexibly master a writing method to solve a class of problems
    private int binary_search(int[] nums, int target) {
    
    
        int left = 0,right = nums.length - 1;
        //  left <= right 能够保证找到目标元素
        //  如果 left < right 判断,有可能存在最终找不到
        while(left <= right) {
    
    
            //  该种写法防止数组下标越界
            int mid = left + (right - left) / 2;
            if(nums[mid] < target) {
    
    
                left = mid + 1;
            } else {
    
    
                // nums[mid] >= target  ,这里最终舍弃掉重复元素的右半部分重复的元素,最终返回左边界
                right = mid - 1;
            }
        }
        // 最终 left = right + 1  最终返回目标元素的下标
        // 确保元素存在还需要进行判断
        //  1.如果寻找的目标元素大于数组的最大元素,那么最终返回 left = 数组长度
        //  2.需要判断 最终寻找到的元素是不是目标元素
        //  3.如果想要找到目标元素的右边界,那么首先需要确定元素存在,然后寻找 target+1 最终得到的结果减1 ,即为右边界
        return left;
    }

Guess you like

Origin blog.csdn.net/GoNewWay/article/details/130333123