二分查找需要给一个查找区间,该查找区间有两种表示方法。
闭区间
l, r = 0, len(arr)-1
左闭右开区间
l, r = 0, len(arr)
因此本文的代码都会给出两个版本,我个人更喜欢闭区间表示。
查找指定元素在数组中的下标值
给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1。
闭区间版本
def search(arr, target) -> int:
l, r = 0, len(arr)-1
while l <= r: # 出口是搜索区间为空
m = (l+r)//2
if arr[m] == target:
return m
if arr[m] < target: # 左闭所以不包括m
l = m + 1
else: r = m - 1 # 右闭所以不包括m
return -1
左闭右开区间版本
def search(arr, target) -> int:
l, r = 0, len(arr)
while l < r: # 出口是搜索区间为空
m = (l+r)//2
if arr[m] == target:
return m
if arr[m] < target: # 左闭所以不包括m
l = m + 1
else: r = m # 右开所以包括m
return -1
二分代码中纠结的点都已经在代码中注释了,l <= r或l < r是因为搜索区间的表示不同,他们都表示着搜索区间为空的情况。r = m或r = m - 1也一样,都是为了继续维护搜索区间右侧边界的开闭而已。
查找指定元素在数组中的左边界
闭区间版本
def search(arr, target) -> int:
l, r = 0, len(arr)-1
while l <= r:
m = (l+r)//2
if arr[m] < target:
l = m + 1
else: r = m - 1
return l
左闭右开区间版本
def search(arr, target) -> int:
l, r = 0, len(arr)
while l < r:
m = (l+r)//2
if arr[m] < target:
l = m + 1
else: r = m
return l
返回值表示其左侧所有元素全部小于target,因此有可能返回len(arr),即全部元素小于target。之所以能够找到左边界的原因在于arr[m] == target时收缩了右边界。
查找指定元素在数组中的右边界
闭区间版本
def search(arr, target) -> int:
l, r = 0, len(arr)-1
while l <= r:
m = (l+r)//2
if arr[m] > target:
r = m - 1
else: l = m + 1
return r
左闭右开区间版本
def search(arr, target) -> int:
l, r = 0, len(arr)
while l < r:
m = (l+r)//2
if arr[m] > target:
r = m
else: l = m + 1
return r - 1
返回值表示其右侧所有元素全部小于target,因此有可能返回-1,即全部元素大于target。之所以能够找到右边界的原因在于arr[m] == target时收缩了左边界。
需要注意的是在左闭右开区间版本返回的是r-1,因此更推荐使用闭区间表示搜索区间。