2024.3.(18,23号)力扣刷题记录-二分查找学习记录1

一、学习视频

二分查找 红蓝染色法_哔哩哔哩_bilibili

二、视频跟练

题目:34. 在排序数组中查找元素的第一个和最后一个位置

代码:

参考视频,代码如下:

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.2529. 正整数和负整数的最大计数

二分查找。正整数个数只用找到正整数开始的位置,其到结尾均为正整数;同理,负整数只用找到负整数结束的位置。正整数开始的位置即为大于等于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))

 2.2300. 咒语和药水的成功对数

(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 

3.275. H 指数 II

当至少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-下标个

 4.2187. 完成旅途的最少时间

(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的时间也能够完成。

5.2439. 最小化数组中的最大值

(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)))

6.2861. 最大合金数 

来自灵神题解(. - 力扣(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 

感谢你看到这里!一起加油吧!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值