灵神视频学习笔记

二分查找 闭区间 开区间 半闭半开区间 》转换
给你一个按照非递减顺序排列的整数数组 nums,和一个目标值 target。请你找出给定目标值在数组中的开始位置和结束位置。
如果数组中不存在目标值 target,返回 [-1, -1]。
你必须设计并实现时间复杂度为 O(log n) 的算法解决此问题。
哪个习惯写哪个,
问题:返回有序数组中第一个≥8的数的位置 如果所有数都<8,返回数组长度
暴力 从左到右 最多on 没有利用数组有序的性质,我们要想尽快的获取到【0,n-1】这个区间内数字与8的关系,最好的方法就是取中间值, 如果列长是偶数,向下取整,(left+right)//2 (java c++ 可以 left+(right-left)//2))

闭区间 lower_round 时间复杂度o logn
L R指针指向的是list的0和n-1

第一次取得中值,位置为M,与target比较 假设小于target,那么说明 M以及小于M位置的数都是小于target的,那么剩下不确定的区间就是【M+1, R】,

所以将L更新成M+1 ,第二次取中值,M更新,假设=target 那么说明【M,R】都满足,不确定的区间就是【L,M-1】,R更新为M-1

继续判断 >=8 R继续更新为M-1 到了L的左边 所有未确定的区间都结束了

最后一次取中值,判断是>=target ,此时R左移在L的左边,二分查找结束,注意此时的结果
L-1始终是红色,也就是<target
R+1始终是蓝色。也就是 >=target 也就是我们需要的答案 还有R+1=L,所以答案可以用L表示
如果查不到比如都比target小 那么L会一直向右移动 L会等于数组长度
def lower_bound(nums,target):
left=0
right=len(nums)-1 #闭区间[left,right]
while left<=right: #区间不为空 这里是<=
mid=left+(right-left)//2
if nums[mid]<target:
left=mid+1 #【mid+1,right】
else:
right=mid-1 #[left,mid-1]
return left
左闭右开
def lower_bound2(nums,target):
left=0
right=len(nums) #左闭右开[left,right)
while left < right: #这里是 <
mid=left+(right-left)//2
if nums[mid]<target:
left=mid+1 #[mid+1 ,right)
else:
right=mid #[left,mid)
return left
全开区间
def lower_bound3(nums,target):
left=-1
right=len(nums) #左闭右开(left,right)
while left +1 < right: #这里是 <
mid=left+(right-left)//2
if nums[mid]<target:
left=mid #(mid+1 ,right)
else:
right=mid #(left,mid)
return right
有序数组中二分查找的四种类型(下面的转换仅适用于数组中都是整数)
问题:返回有序数组中第一个≥8的数的位置 如果所有数都<8,返回数组长度

  1. 查找第一个大于等于x的下标: low_bound(x)
  2. 查找第一个大于x的下标:可以转换为第一个大于等于 x+1 的下标 ,low_bound(x+1)
  3. 查找最后一个小于x的下标:可以转换为第一个大于等于 x 的下标左边位置, low_bound(x) - 1; <X -> (>=X) -1
  4. 查找最后一个小于等于x的下标:可以转换为第一个大于等于 x+1 的下标左边位置, low_bound(x+1) -1 <=X -> >=(X+1)-1

理解:先找到 >= target+1 的第一个数,那么它左边那个数就是 <= target 的最后一个数。

上面理解了再来看
做个总结:如果是用上面的lower_round二分 那么其结果就是第一个>=target的左边,通过这个>= 我们可以通过上面转换获得各类输出,下面就是题目
34. 在排序数组中查找元素的第一个和最后一个位置
给你一个按照非递减顺序排列的整数数组 nums,和一个目标值 target。请你找出给定目标值在数组中的开始位置和结束位置。
如果数组中不存在目标值 target,返回 [-1, -1]。
你必须设计并实现时间复杂度为 O(log n) 的算法解决此问题。
def lower_bound(nums,target):
#ologn
left=0
right=len(nums)-1 #闭区间[left,right]
while left<=right: #区间不为空 这里是<=
mid=left+(right-left)//2
if nums[mid]<target:
left=mid+1 #【mid+1,right】
else:
right=mid-1 #[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]

开始位置就是第一个>=target ,就是相当于第一个>=target,所以直接调用lower_bound(nums,target)
结束位置也就是最后一个<=target 将其转换为 (>=target+1)-1 也就是
Lower_round(nums,target+1)-1
作业
2529. 正整数和负整数的最大计数
给你一个按 非递减顺序 排列的数组 nums ,返回正整数数目和负整数数目中的最大值。
• 换句话讲,如果 nums 中正整数的数目是 pos ,而负整数的数目是 neg ,返回 pos 和 neg二者中的最大值。
注意:0 既不是正整数也不是负整数。
def lower_round(nums,target):
left=0
right=len(nums)-1
while left <= right:
mid=left+(right-left)//2
if nums[mid]<target :
left=mid+1
else:
right=mid-1
return left
class Solution:
def maximumCount(self, nums: List[int]) -> int:
last_low_zero=lower_round(nums,0)-1

    first_up_zero=lower_round(nums,1)
    
    neg=len( nums[0:last_low_zero+1] )
    pos=len( nums[first_up_zero:]  )
    return max(pos,neg)

  1. 咒语和药水的成功对数
    给你两个正整数数组 spells 和 potions ,长度分别为 n 和 m ,其中 spells[i] 表示第 i 个咒语的能量强度,potions[j] 表示第 j 瓶药水的能量强度。
    同时给你一个整数 success 。一个咒语和药水的能量强度 相乘 如果 大于等于 success ,那么它们视为一对 成功 的组合。
    请你返回一个长度为 n 的整数数组 pairs,其中 pairs[i] 是能跟第 i 个咒语成功组合的 药水 数目。
    def lower_round(nums,target):
    left=0
    right=len(nums)-1
    while left <= right:
    mid=left+(right-left)//2
    if nums[mid]<target :
    left=mid+1
    else:
    right=mid-1
    return left

class Solution:
def successfulPairs(self, spells: List[int], potions: List[int], success: int) -> List[int]:

    m=len(potions)
    potions.sort()   
        
    return [ m - lower_round(potions,   success/x)   for x in spells]

补充bisect_left()
bisect()和bisect_right()等同,那下面就介绍bisect_left()和bisec_right()的区别!
用法:
index1 = bisect(ls, x) #第1个参数是列表,第2个参数是要查找的数,返回值为索引
index2 = bisect_left(ls, x)
index3 = bisec_right(ls, x)
bisect.bisect和bisect.bisect_right返回大于x的第一个下标(相当于C++中的upper_bound),bisect.bisect_left返回大于等于x的第一个下标(相当于C++中的lower_bound)。
2.1 bisect_left()
bisect.bisect_left(a, x, [lo=0, hi=len(a)]):
在序列 a 中二分查找适合元素 x 插入的位置,保证 a 仍为 有序序列。
若序列 a 中存在与 x 相同的元素,则返回与 x 相同的第一个 (最左侧) 元素的位置索引,使得 x 若插入后能位于其 左侧;

若序列 a 中不存在与 x 相同的元素,则返回与 x 右侧距离最近的元素位置索引,使得 x 若插入后能位于其 左侧。
而 lo 和 hi 为可选参数,分别定义查找范围/返回索引的 上限和下限,缺省时默认对 整个 序列查找。
因此,返回的位置索引 又称为 “插入点”,将其设为 i,则序列 a 可以被划分为两部分,切片表示左侧 a[lo, i) 和右侧 a[i, hi] 。
bisect.bisect_right(a, x, lo=0, hi=len(a), *, key=None)
bisect.bisect(a, x, lo=0, hi=len(a), *, key=None)
类似于 bisect_left(),但是返回的插入点是在 a 中任何现有条目 x 之后(即其右侧)。
返回的插入点 ip 将数组 a 分为两个切片使得对于左侧切片 all(elem <= x for elem in a[lo : ip]) 为真值而对于右则切片 all(elem > x for elem in a[ip : hi]) 为真值。
在 3.10 版本发生变更: 增加了 key 形参。

  • 20
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值