二分查找(折半查找)的各种版本
网上各类零零散散的二分查找,很多都没遵循前闭后开,还有很多代码运行到最后要额外判断left和right,运行是没问题,但是并不优雅,有强迫症的我看着很不舒服。
以下我自己总结一下二分查找的各种版本,递归实现。(水平有限,如有错误敬请指正)
注:以下代码及总结遵循前闭后开原则:begin为起始下标,end为末尾下标的后一个,与STL等库统一。
先上总结:
套路十分简单!只要改改下次递归区间和mid的取整方式,一切迎刃而解!
以下归纳不包括查找恰好等于target的算法,恰好等于的单独讨论。
另外,以下算法不考虑结果不在数组内的情况,这个在外层特殊判断即可。(比如数组[1,2]要查找第一个大于2的,这个结果不在数组内)
什么时候结束:
begin + 1 == end时,说明区间里只有唯一一个数。
很多地方都用大于等于、小于等于之类的实现。这里精确到等于号,不稀里糊涂。(有些版本用≥、≤当然没错,但有时到最后要判断左右两值,虽然也具有很好的通用性,但是没那么紧凑。)
下一次递归的区间:
下一次递归往左还往右:target小于nums[mid]往左,大于nums[mid]往右。然后再判断等于的情况往哪。
具体的区间是什么:重点就在于,nums[mid]应该包含在左边的区间还是右边的区间,根据情况决定
mid到底是向上还是向下取整:
如果下一次递归,nums[mid]放在左区间就向下取整,放在右区间就向上取整。
("//"在Python里是整除的意思,其他语言自行修改)
原因:若nums[mid]放在左区间,那么num[mid]就会成为下一次递归中最右边的一个,如果这时候是向上取整,那么mid值可能依然是最右边的一个元素(比如两个元素的数组[0,1]如果用向上取整求mid,结果是1),导致死循环。
遵循前闭后开原则中,begin + (end - begin - 1) // 2是向下取整,不减1是向上取整
查找恰好等于target的(如果要查找第一个恰好等于的或者最后一个恰好等于的,根据后面的>=和<=修改即可)
# 二分查找,返回下标,不存在返回-1
# 如果要查找第一个恰好等于的或者最后一个恰好等于的,根据后面的>=和<=修改下即可
def binary_search_1(nums, target, begin, end):
if begin == end:
return -1
mid = begin + (end - begin - 1) // 2
if target < nums[mid]:
return binary_search_1(nums, target, begin, mid)
elif target > nums[mid]:
return binary_search_1(nums, target, mid + 1, end)
else:
return mid
查找第一个>=target的(若相等则返回最左边的)
def binary_search_2(nums, target, begin, end):
if begin + 1 == end:
return begin
mid = begin + (end - begin - 1) // 2
if target <= nums[mid]:
return binary_search_2(nums, target, begin, mid + 1)
else:
return binary_search_2(nums, target, mid + 1, end)
查找最后一个<=target的(若相等则返回最右边的)
def binary_search_3(nums, target, begin, end):
if begin + 1 == end:
return begin
mid = begin + (end - begin) // 2
if target < nums[mid]:
return binary_search_3(nums, target, begin, mid)
else:
return binary_search_3(nums, target, mid, end)
查找第一个>target的
def binary_search_4(nums, target, begin, end):
if begin + 1 == end:
return begin
mid = begin + (end - begin - 1) // 2
if target < nums[mid]:
return binary_search_4(nums, target, begin, mid + 1)
else:
return binary_search_4(nums, target, mid + 1, end)
查找最后一个<target的
def binary_search_5(nums, target, begin, end):
if begin + 1 == end:
return begin
mid = begin + (end - begin) // 2
if target <= nums[mid]:
return binary_search_5(nums, target, begin, mid)
else:
return binary_search_5(nums, target, mid, end)