二分搜索
二分搜索类型
因为这样的性质,二分有传说中的二分模版和各种边界条件,个人也有这里我觉得可以分两种情况讨论:
二分存在性搜索:在whlie循环中返回答案,循环中
l<=r
二分最值搜索:在while循环后返回答案,循环中
l<r
边界搜索:在while循环后返回答案,循环中
l<=r
看模版中的注释来看细节。在判断区间时,总是用中点m
来缩小区间至左边或右边。
参考模版:
"""
情况一:二分存在性搜索 - 在whlie循环中返回答案
寻找target
循环:用l<=r 为条件开始搜索,这样如果中点m每次没有找到,
就可以直接避开m缩小返回。
判断:如果中点符合条件,直接返回。
如果不在[l,r]左区间则缩小至右区间搜索l=m+1
否则r=m-1在左区间搜索
退出:如果l和r完美错过,那么没找到
"""
l, r = 0, len(nums)-1
while l<=r:
m = (+r)>>1
if nums[m]==n: # 找到了
return m
if {...}: # 判断:如果不在[l, m]闭区间
l = m+1
else:
r = m-1
return -1 # 没找到
情况一:存在性查找例题
二分查找(Binary)
搜索旋转排序数组(Rotated I)
搜索旋转排序数组 II(Rotated II)
def search(nums, target):
l, r = 0, len(nums)-1
while l<=r:
m = (l+r) >> 1
if nums[m]==target:
return m
if target>nums[m]:
l = m+1
else:
r = m-1
return -1
情况二:最值查找例题
寻找峰值(Peak)
寻找旋转排序数组中的最小值(Rotated Min I)
寻找旋转排序数组中的最小值 II(Rotated Min II)
山脉数组中查找目标值(Mountain)
def findPeakElement(nums):
l, r = 0, len(nums)-1
while l<r:
m = l+(r-l) // 2
if nums[m+1]>nums[m]:
l = m+1
else:
r = m
return l
情况三:边界搜索
极小极大化:本质就是用二分来找找到第一个/最后一个可行解,因为可行解都在答案的一侧。
把可能答案从小到大排列,可以发现可行性的排列大概是✖✖✖✖✔✔✔。我们极小极大化的答案就是第一个✔。而验证答案,即判断是✖还是✔这个过程,很多时候是用贪心来验证的,如果假设答案不可行就返回错误,可行,则判断是可能解再在左区间搜索。
模版:第一个错误的版本(bad)
分享巧克力(chocolate)
在 D 天内送达包裹的能力(package)
爱吃香蕉的珂珂(koko)
分割数组的最大值(split)
制作 m 束花所需的最少天数(flower)
完成所有工作的最短时间 (work)(回溯方法和剪枝:698. 划分为k个相等的子集)
H 指数 II (h-index)
left, right = 1, n
while left <= right:
mid = (left + right) >> 1
if isBadVersion(mid):
right = mid - 1
else:
left = mid + 1
return left
bisect库函数
Python的bisect库让人挺偷懒的,一个具体的例子,在排序数组中查找元素的第一个和最后一个位置,相对对应着bisect的这两个方法:
# 元素target第一个插入点
# 如果在数组的末尾(所有数字都比target小),那么元素不存在
l = bisect.bisect_left(nums, target)
# 元素target的最后插入点
# 如果在数组的开头(所有数字都比target大),那么元素不存在
r = bisect.bisect_right(nums, target)
为了不偷懒,我们分别把这两个方法实现一下。
def bisect_left(nums, target):
l, r = 0, len(nums)-1
while l<=r:
m = (l+r) >> 1
if nums[m]>=target:
r = m-1
else:
l = m+1
return l
def bisect_right(nums, target):
l, r = 0, len(nums)-1
while l<=r:
m = (l+r) >> 1
if target>=nums[m]:
l = m+1
else:
r = m-1
# 因为bisect_right找的是插入点而非存在点
# 所以如果target存在,返回它的下一个索引
return l
def searchRange(nums, target):
l = bisect.bisect_left(nums, target)
r = bisect.bisect_right(nums, target)
n = len(nums)
l = l if l!=n and nums[l]==target else -1
r = r-1 if r and nums[r-1]==target else -1
return [l, r]
Last updated
Was this helpful?