针对二分查找查找目标元素的各种情况,以及每一行代码的不同写法的组合所达到的不同效果
前置说明
- 关于左右边界的选择
- 开区间是指左右边界初始化选取不包括数组内部元素,闭区间反之
- 左边界分为开区间和闭区间两种情况
- 开区间
left = -1
- 闭区间
left = 0
- 开区间
- 右边界
- 开区间:
right = n
- 闭区间:
right = n - 1
- 开区间:
- 关于跳出循环的条件判断
left < right
:最终跳出循环->left = right + 1
left <= right
:->left = right
- 关于
mid
的计算- 一般
mid = left + (right - left) / 2
- 在java中防止超出数据范围
ex: left = 0,right = 1 那么 mid = 0
- 也就是说:以此种方式计算,最终的结果总会落到左边
- 使结果落到右边的写法
mid = left + (right - left + 1)/2
ex: left = 0,right = 1 那么 mid = 1
- 一般
- 关于条件判断
nums[mid] >= target
- 符合条件的情况下,目标元素在左半部分,更新右边界
实际举例测试各种写法
- 示例数组: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
- 测试结果如下
- 注意事项归纳
- 判断条件为
left <= right
的情况下,最终返回left = right + 1
- 判断
nums[mid] >= target; right = mid - 1
- 此时如果
nums[mid] = target
目标值将会被舍去 - 因为对应的判断是
nums[mid] < target; left = mid + 1
- 很显然最终返回
left
是行之有效的方法
- 很显然最终返回
- 此时如果
- 边界情况说明
- 如果查找目标数组的第一和最后一个元素
- 查找第一个元素:
target = 1,left = 0,right = -1
right的值不断减小直到等于-1
- 查找最后一个元素:
target = 19,left = 9,right = 8
left的值不断增加直到最后 left = 9,right = 8
- 返回
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
.该如何写 - 反之想要返回“右边”索引,该如何写。
- 或者总期望返回目标元素左边或右边的元素,该如何写
- 对上面返回
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