踩坑二分查找-进阶

针对二分查找查找目标元素的各种情况,以及每一行代码的不同写法的组合所达到的不同效果

前置说明

  1. 关于左右边界的选择
    1. 开区间是指左右边界初始化选取不包括数组内部元素,闭区间反之
    2. 左边界分为开区间和闭区间两种情况
      1. 开区间 left = -1
      2. 闭区间 left = 0
    3. 右边界
      1. 开区间:right = n
      2. 闭区间:right = n - 1
  2. 关于跳出循环的条件判断
    1. left < right:最终跳出循环->left = right + 1
    2. left <= right:->left = right
  3. 关于mid的计算
    1. 一般mid = left + (right - left) / 2
      1. 在java中防止超出数据范围
      2. ex: left = 0,right = 1 那么 mid = 0
      3. 也就是说:以此种方式计算,最终的结果总会落到左边
    2. 使结果落到右边的写法 mid = left + (right - left + 1)/2
      1. ex: left = 0,right = 1 那么 mid = 1
  4. 关于条件判断
    1. nums[mid] >= target
      1. 符合条件的情况下,目标元素在左半部分,更新右边界

实际举例测试各种写法

  • 示例数组:no_repeat_arr = [1, 3, 4, 5, 6, 7, 9, 11, 14, 19]

查找的目标元素存在

  • 如果目标元素存在,则写法较为宽松
def binary_search(nums: List[int], target: int) -> int:
    left, right = 0, len(nums) -1
    while left <= right:
        mid = left + (right - left) // 2
        if nums[mid] >= target:
            right = mid - 1
        else:
            left = mid + 1
            # left = right + 1
    return left
  • 测试结果如下

image.png

  1. 注意事项归纳
  2. 判断条件为left <= right的情况下,最终返回left = right + 1
  3. 判断nums[mid] >= target; right = mid - 1
    1. 此时如果nums[mid] = target目标值将会被舍去
    2. 因为对应的判断是nums[mid] < target; left = mid + 1
      1. 很显然最终返回left是行之有效的方法
  4. 边界情况说明
    1. 如果查找目标数组的第一和最后一个元素
    2. 查找第一个元素:target = 1,left = 0,right = -1
      1. right的值不断减小直到等于-1
    3. 查找最后一个元素:target = 19,left = 9,right = 8
      1. left的值不断增加直到最后 left = 9,right = 8
  5. 返回left和返回right的两种写法
# return left
def binary_search_1(nums: List[int], target: int) -> int:
    left, right = 0, len(nums) - 1
    while left <= right:
        mid = left + (right - left) // 2
        # 右半部分元素,包括目标元素被舍弃,所以最终返回 left
        if nums[mid] >= target:
            right = mid - 1
        else:
            left = mid + 1
    return left


# return right
def binary_search(nums: List[int], target: int) -> int:
    left, right = 0, len(nums) - 1
    while left <= right:
        mid = left + (right - left) // 2
        # 左半部分元素,包括目标元素被舍弃,所以最终返回 right
        if nums[mid] <= target:
            left = mid + 1
        else:
            right = mid - 1
    return right

查找的目标元素不存在

思考这样一个特殊的场景,需要去寻找目标数组中的一个元素,但是不确定该元素存在与否。想要达到一个预期效果:如果目标元素存在,则返回目标元素索引,如果不存在。ex: [1, 9] target = 8

  1. 想要返回目标元素1的索引,即查找的元素的左边,如果查找的元素小于数组中的元素,希望返回-1.该如何写
  2. 反之想要返回“右边”索引,该如何写。
  3. 或者总期望返回目标元素左边或右边的元素,该如何写
  4. 对上面返回left or right的代码稍作改动即可
def binary_search(nums: List[int], target: int) -> int:
    left, right = 0, len(nums) - 1
    while left <= right:
        mid = left + (right - left) // 2
        # 右边部分元素被舍弃
        if nums[mid] >= target:
            right = mid - 1
        else:
            left = mid + 1
    # 边界情况特殊处理
    if len(nums) == left:
        return len(nums) - 1
    if left == -1:
        return -1
    return left - 1 if nums[left] == target else left
def binary_search(nums: List[int], target: int) -> int:
    left, right = 0, len(nums) - 1
    while left <= right:
        mid = left + (right - left) // 2
        if nums[mid] <= target:
            left = mid + 1
        else:
            right = mid - 1
    return left

猜你喜欢

转载自blog.csdn.net/GoNewWay/article/details/130566677
今日推荐