一、学习视频
二、视频跟练
代码:
参考视频,代码如下:
def lower_bound(nums: List[int], target: int) -> int: # >=target
n = len(nums)
left,right = 0,n-1 #闭区间
while left <= right: #区间不为空 等于时n=1,也不为空
mid = (left+right) // 2
#mid = (left+right+1) // 2 #均可以
if nums[mid] >= target:
right = mid - 1
else:
left = mid + 1
return left
class Solution:
def searchRange(self, nums: List[int], target: int) -> List[int]:
start = lower_bound(nums,target)
if start == len(nums) or nums[start] != target: #注意第二个
return [-1,-1] #不存在
end = lower_bound(nums,target+1)-1
return [start,end]
注意:1.统一区间符号;2.大于小于等于符号及转换关系;3.注意while那里的要求是遍历完所有的元素,即闭区间为空(l==r不为空)。
时复O(logn),空复O(1)。
三、课后作业练习
二分查找。正整数个数只用找到正整数开始的位置,其到结尾均为正整数;同理,负整数只用找到负整数结束的位置。正整数开始的位置即为大于等于1的位置,负整数结束的位置即为大于等于0的位置减一。代码如下:
class Solution:
def maximumCount(self, nums: List[int]) -> int:
def lower_bound(nums: List[int],target:int) -> int:
n = len(nums)
left,right = 0,n-1
while left<=right:
mid = (left+right) // 2
if nums[mid] >= target:
right = mid-1
else:
left = mid+1
return left
# 返回坐标
posstart = lower_bound(nums,1)
negend = lower_bound(nums,0)-1
return max(len(nums)-posstart,negend+1)
或使用二分查找库bisect,参考灵神题解(. - 力扣(LeetCode))。代码如下:
class Solution:
def maximumCount(self, nums: List[int]) -> int:
# 二分查找库
# bisect_left(nums,0)查找大于等于0的坐标,正好是负整数末尾坐标加一,即个数
# bisect_right(nums,0)查找大于0的坐标
return max(bisect_left(nums,0), len(nums)-bisect_right(nums,0))
(1)二分查找+排序
class Solution:
def successfulPairs(self, spells: List[int], potions: List[int], success: int) -> List[int]:
# 二分查找+排序
# 时复O(nlogn)
# 遍历每一个咒语,药水值大于等于success除咒语值
n = len(spells)
m = len(potions)
ans = [0]*n
potions.sort() #二分需要有序
for i in range(n):
target = success / spells[i]
ans[i] = m - bisect_left(potions,target)
return ans
遍历时复O(n^2)。
2024.3.23续:
(2)双指针解法,方法来自官方题解(. - 力扣(LeetCode))。
class Solution:
def successfulPairs(self, spells: List[int], potions: List[int], success: int) -> List[int]:
# 双指针,时复O(n)
ans = [0]*len(spells)
idx = [i for i in range(len(spells))]
idx.sort(key=lambda x: spells[x]) #对对应下标排序,这样不用改变原位置
potions.sort(reverse = True) #降序
j = 0
for p in idx:
v = spells[p]
while j < len(potions) and potions[j] * v >= success:
j += 1
ans[p] = j
#下次循环j不用从0开始,药水 * 小咒语 >= success,药水 * 大咒语肯定大于success
#只需要验证小药水*大咒语是不是大于success
return ans
当至少4篇引用量至少4次时,肯定有至少3篇引用量至少3次。因为是排序数组,与下标建立联系。那么可以从左开始,若下标i(n-i篇)引用量满足,则i+1(n-(i+1)篇)引用量必定满足。而“满不满足条件”可以看成“下标和对应值的关系”当下标i引用量满足条件时,则i右边也满足条件(包括i共n-i篇)满足条件,此时h=n-i;而引用量需要大于等于h,则需citations[i] >= n-i。
(1)遍历,时复O(n)。
class Solution:
def hIndex(self, citations: List[int]) -> int:
# 遍历,时复O(n)
n = len(citations)
for i in range(n):
if citations[i] >= n-i:
return n-i
return 0
(2)二分查找,时复O(logn)。
class Solution:
def hIndex(self, citations: List[int]) -> int:
# 二分查找,时复O(logn)
n = len(citations)
l,r = 0,n-1
while l <= r:
mid = (l + r) // 2
if citations[mid] >= n - mid:
#右边的都满足要求,这时看看最低多少满足,左移
r = mid - 1
else:
l = mid + 1
return n-l #个数n-下标个
(1)二分函数,一行。来自灵神题解(. - 力扣(LeetCode))。
class Solution:
def minimumTime(self, time: List[int], totalTrips: int) -> int:
#二分函数,一行
return bisect_left(range(totalTrips*min(time)),totalTrips,key = lambda x :sum(x//t for t in time))
#key即为指针移动判断条件
(2)二分,参考官方题解(. - 力扣(LeetCode))。
class Solution:
def minimumTime(self, time: List[int], totalTrips: int) -> int:
#二分
#判断 x 时间内是否可以完成 totalTrips 趟旅途
def check(x:int) -> bool:
cnt = 0 #旅程数
for t in time:
cnt += x // t
return cnt >= totalTrips
# 指针是时间
l,r = 1,totalTrips * min(time)
while l <= r:
mid = (l + r) // 2
if check(mid):
r = mid - 1
else:
l = mid + 1
return l
二分的关键是有序性。在time时间能够完成,那么time+1的时间也能够完成。
(1)二分,来自灵神题解(. - 力扣(LeetCode))。
class Solution:
def minimizeArrayValue(self, nums: List[int]) -> int:
#二分
def check(limit:int)->bool:
extra = 0
for i in range(len(nums)-1,0,-1):
extra = max(nums[i] + extra - limit, 0)
return nums[0] + extra <= limit
return bisect_left(range(max(nums)), True, key=check)
(2)取前i项平均值,来自灵神题解。其中【ceil(s)/ i+1】==【(s + i) // (i + 1)】。
再参考题解(. - 力扣(LeetCode))进行理解,他说:不变的是sum,变的只是分配。那下面代码的本质是对前i项进行分配,得一个最小的最大值(即平均)。再对所有所得值取最大值(所得值为局部(最小的)最大值,后面可能存在更大的值)。
class Solution:
def minimizeArrayValue(self, nums: List[int]) -> int:
# 取前i项平均值
return max((s + i) // (i + 1) for i,s in enumerate(accumulate(nums)))
来自灵神题解(. - 力扣(LeetCode))。改为使用闭区间。
class Solution:
def maxNumberOfAlloys(self, n: int, k: int, budget: int, composition: List[List[int]], stock: List[int], cost: List[int]) -> int:
# 二分查找,这里查找的是合金个数num
ans = 0
max_ = min(stock) + budget #上界
for comp in composition:
def check(num:int) -> bool:
money = 0
for s,base,c in zip(stock,comp,cost):
if s < base * num:
money += (base * num - s) * c
if money > budget:
return False
return True
l,r = ans,max_ #ans <= num <= max_
while l <= r:
mid = (l + r) // 2
if check(mid):
# 能生产mid个零件,则一定能生产mid-1个零件
l = mid + 1
else:
r = mid - 1
ans = l #L为未查找,所以return要减一
return ans-1
完
感谢你看到这里!一起加油吧!