不多解释,按照我写了一个套路,助你随心所欲运用二分搜索一文中的模板套路,我们看一下python版本:
普通的二分查找:
def binary_search(nums, target):
left, right = 0, len(nums)-1
# 搜索区间[left,right]
while left <= right:
mid = left + (right - left) //2
if nums[mid] == target:
# 直接返回
return mid
elif nums[mid] < target:
left = mid + 1
elif nums[mid] > target:
right = mid - 1
return -1
搜索左边界:
def left_bound(nums, target):
left, right = 0, len(nums)-1
while left <= right:
mid = left + (right-left)//2
if nums[mid] == target:
#不返回,锁定左侧边界
right = mid -1
elif nums[mid] < target:
left = mid + 1
elif nums[mid] > target:
right = mid - 1
if left >=len(nums) or nums[left] != target:
return -1
# 返回左侧边界
return left
搜索右边界,拿上面的代码稍微按规律变一下就可以:
def right_bound(nums, target):
left, right = 0, len(nums)-1
while left <= right:
mid = left + (right-left)//2
if nums[mid] == target:
#不返回,锁定右侧边界
left = mid + 1
elif nums[mid] < target:
left = mid + 1
elif nums[mid] > target:
right = mid - 1
if left < 0 or nums[right] != target:
return -1
# 返回左侧边界
return right
拿这个模板就能直接用了吗,并不能,我们还需要做很多的变换,还需要从具体的问题中抽象出模型出来:
在上文中,我们知道了我们需要找到一个单调函数f(x),以及对应的自变量x,还有那个target,针对不同的问题我们抽象出不同的[f(x),x,target],再根据题目要求找到初始化时搜索区间的左右边界,以及边界是开区间还是闭区间、确定这是不是一个搜索左右边界的问题,最后还需要确定当f(nums,mid) == target的时候,我们需要做什么,以及对应返回值应该是什么。
比如最传统的二分查找可以泛化为:
def f(nums, x):
return nums[x]
def binary_search(nums, target):
left, right = 0, len(nums)-1
# 搜索区间[left,right]
while left <= right:
mid = left + (right - left) //2
if f(nums, mid) == target:
# 直接返回
return mid
elif f(nums, mid) < target:
left = mid + 1
elif f(nums, mid) > target:
right = mid - 1
return -1
具体的分析细节labuladong在他的文章里边已经分析的很透彻了,这里不再赘述,简单给出两道例题的python版本:
875. 爱吃香蕉的珂珂
class Solution:
def minEatingSpeed(self, piles: List[int], h: int) -> int:
def f(piles,k):
hour = 0
for pile in piles:
hour += pile//k
if pile%k > 0:
hour += 1
return hour
left = 1
right = 1000000000 + 1
while left < right:
mid = left + (right - left)//2
if f(piles,mid) <= h:
right = mid
else:
left = mid + 1
return left
1011. 在 D 天内送达包裹的能力
class Solution:
def shipWithinDays(self, weights: List[int], days: int) -> int:
def f(weights,x):
total, days, i = 0, 0, 0
while i < len(weights):
if weights[i] > x-total:
days += 1
total = 0
total += weights[i]
i += 1
return days+1
left = max(weights)
right = sum(weights) + 1
while left < right:
mid = left + (right -left)//2
if f(weights,mid) <= days:
right = mid
else:
left = mid + 1
return left
需要仔细体会这种套路的精髓。