defbinary_search_list(nums:[list[int]], target:int)->int: result = [] i, j = 0, len(nums)-1 while i<=j: m = (i+j)//2 if nums[m]<target: i = m+1 elif nums[m]>target: j = m-1# 左闭右开则为 j = m else: result.append(m) # 向左查找 left = m - 1 while left>=0and nums[left]==target: indices.append(left) left -= 1 # 向右查找 right = m + 1 while right<len(nums) and nums[right]==target: indices.append(right) right += 1 break return result
值得注意的是,由于 i 和 j 都是 int 类型,因此 i+j 可能会超出 int 类型的取值范围;为避免大数越界通常采用公式 i+2j−i来计算中点。
适用场景:
有序数据
数组
数据量较大
扩展 1:二分查找插入点
当数组中无重复元素时,只需将二分查找元素代码最后一行返回值换成 i 即可。
函数将返回最后一个小于目标值的后一个索引(当数组中最小值仍大于目标值时,索引将返回 0 ;当数组中最大值仍小于目标值时,索引将返回 len(list))。
1 2 3 4 5 6 7 8 9 10 11 12 13
defbinary_search_insertion_simple(nums: list[int], target: int) -> int: """二分查找插入点(无重复元素)""" i, j = 0, len(nums) - 1# 初始化双闭区间 [0, n-1] while i <= j: m = (i + j) // 2# 计算中点索引 m if nums[m] < target: i = m + 1# target 在区间 [m+1, j] 中 elif nums[m] > target: j = m - 1# target 在区间 [i, m-1] 中 else: return m # 找到 target ,返回插入点 m # 未找到 target ,返回插入点 i return i
defbinary_search_insertion(nums: list[int], target: int) -> int: """二分查找插入点(存在重复元素)""" i, j = 0, len(nums) - 1# 初始化双闭区间 [0, n-1] while i <= j: m = (i + j) // 2# 计算中点索引 m if nums[m] < target: i = m + 1# target 在区间 [m+1, j] 中 elif nums[m] > target: j = m - 1# target 在区间 [i, m-1] 中 else: j = m - 1# 首个小于 target 的元素在区间 [i, m-1] 中 # 返回插入点 i return i
我们发现,左闭右开更适合这类“边界”问题。
1 2 3 4 5 6 7 8 9 10 11 12
defbinary_search_lcro_insertion(nums:[list[int]], target:int)->int: # nums已排序 i, j = 0, len(nums)-1 while i < j: m = (i+j)//2 if nums[m] < target: i = m+1 elif nums[m] > target: j = m else: j = m return i