leetcode刷题的二分搜索技巧【Python】

4 二分搜索

二分查找的常用场景:寻找一个数、寻找左侧边界、寻找右侧边界。

先上框架:

def binary_search(nums,target):
    """非递归
    对有序数组可以寻找下标
    """
    if len(nums)==0:
        return -1
    #nums=sorted(nums)
    l,r = 0, len(nums)-1
    while l<=r:
        mid = l + (r-l)//2
        if nums[mid]==target:
            return mid
        elif nums[mid]>target:
            r = mid-1
        else:
            l = mid +1
    return -1
binary_search([1,8],2)
def binary_search(nums,target):
    """递归"""
    if not nums:
        return False
    #l,r = 0, len(nums)-1
    nums=sorted(nums)
    #print(sorted(nums))
    mid = len(nums)//2
    
    if nums[mid]==target:
        return True
    elif nums[mid]>target:
        print(nums)
        return binary_search(nums[:mid],target)
    else:
        print(nums)
        return binary_search(nums[mid+1:],target)

binary_search([2,1,8],2)
def left_bound(nums,target):
    """非递归"""
    if len(nums)==0:
        return -1
    l,r = 0, len(nums)-1
    while l<=r:
        mid = l + (r-l)//2
        if nums[mid]==target:
            #return mid
            r = mid -1#因为要看左边界,所以先锁定右边界
        elif nums[mid]>target:
            r = mid-1
        else:
            l = mid +1
    #检查L越界情况
    if l>=len(nums):
        return -1
    if nums[l]!=target:
        return -1
    return l

left_bound([1,7,8,8],8)
def right_bound(nums,target):
    """右侧边界"""
    if len(nums)==0:
        return -1
    l,r = 0, len(nums)-1
    while l<=r:
        mid = l + (r-l)//2
        if nums[mid]==target:
            #return mid
            l = mid +1#因为要看左边界,所以先锁定右边界
        elif nums[mid]>target:
            r = mid-1
        else:
            l = mid +1
    #检查L越界情况
    if r<0:
        return -1
    if nums[l]!=target:
        return -1
    return r

right_bound([1,7,8,8],8)

704.二分查找(简单)

给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1。

示例 1:

输入: nums = [-1,0,3,5,9,12], target = 9

输出: 4

解释: 9 出现在 nums 中并且下标为 4

示例 2:

输入: nums = [-1,0,3,5,9,12], target = 2

输出: -1

解释: 2 不存在 nums 中因此返回 -1

def binary_search(nums,target):
    """非递归
    对有序数组可以寻找下标
    """
    if len(nums)==0:
        return -1
    #nums=sorted(nums)
    l,r = 0, len(nums)-1
    while l<=r:
        mid = l + (r-l)//2
        if nums[mid]==target:
            return mid
        elif nums[mid]>target:
            r = mid-1
        else:
            l = mid +1
    return -1

nums = [-1,0,3,5,9,12] 
target = 9
binary_search(nums,target)

34.在排序数组中查找元素的第一个和最后一个位置(中等)

给定一个按照升序排列的整数数组 nums,和一个目标值 target。找出给定目标值在数组中的开始位置和结束位置。

如果数组中不存在目标值 target,返回 [-1, -1]。

进阶:

你可以设计并实现时间复杂度为 O(log n) 的算法解决此问题吗?

示例 1:

输入:nums = [5,7,7,8,8,10], target = 8

输出:[3,4]

示例 2:

输入:nums = [5,7,7,8,8,10], target = 6

输出:[-1,-1]

class left_right_bound(object):
    def left_bound(self,nums,target):
        """左边界"""
        if not nums:
            return -1
        l ,r =0 ,len(nums)-1
        while l<=r:
            mid = l + (r-l)//2
            if nums[mid]>target:
                r = mid -1
            elif nums[mid]<target:
                l = mid +1
            else:
                r = mid -1
        if l>=len(nums):
            return -1
        if nums[l]!=target:
            return -1
        return l
    
    def right_bound(self,nums,target):
        """右边界"""
        if not nums:
            return -1
        l ,r =0 ,len(nums)-1
        while l<=r:
            mid = l + (r-l)//2
            if nums[mid]>target:
                r = mid -1
            elif nums[mid]<target:
                l = mid +1
            else:
                l = mid +1
        if r<0:
            return -1
        if nums[l]!=target:
            return -1
        return r
    
    def result(self,nums,target):
        rb=self.right_bound(nums,target)
        lb=self.left_bound(nums,target)
        return [lb,rb]
    
if __name__=='__main__':
    nums = [5,7,7,8,8,10]
    target = 6
    s=left_right_bound()
    print(s.result(nums,target))

875.爱吃香蕉的珂珂(中等)

珂珂喜欢吃香蕉。这里有 N 堆香蕉,第 i 堆中有 piles[i] 根香蕉。警卫已经离开了,将在 H 小时后回来。

珂珂可以决定她吃香蕉的速度 K (单位:根/小时)。每个小时,她将会选择一堆香蕉,从中吃掉 K 根。

如果这堆香蕉少于 K 根,她将吃掉这堆的所有香蕉,然后这一小时内不会再吃更多的香蕉。

珂珂喜欢慢慢吃,但仍然想在警卫回来前吃掉所有的香蕉。

返回她可以在 H 小时内吃掉所有香蕉的最小速度 K(K 为整数)。

示例 1:

输入: piles = [3,6,7,11], H = 8

输出: 4

示例 2:

输入: piles = [30,11,23,4,20], H = 5

输出: 30

示例 3:

输入: piles = [30,11,23,4,20], H = 6

输出: 23

提示: 1 <= piles.length <= 10^4

piles.length <= H <= 10^9

1 <= piles[i] <= 10^9

思路分析:这道题采用二分搜索。 首先我们将所有的堆按照升序排序,则最慢的速度left是将所有的时间花在最小的堆,最快的速度right是每个小时吃掉最大的堆的数量,我们每次取中值mid = (left + right) / 2,然后按照mid的这个吃香蕉的速度去计算吃掉所有堆的总时间,并且和H判断,然后更新[left, right]区间。

有一点需要注意一下,就是题干要求当我们吃完某个堆还剩余一段时间(不足一小时),我们不能转移到其他堆,只能停在当前堆。比如吃的速度为4,某个堆大小为6,第一个小时吃掉4根,第二个小时中吃掉剩余的2根,这时还剩余一段时间,只能停在此处。所有一共需要的时间 (堆的大小+吃香蕉的速度 - 1)/ 吃香蕉的速度或者(堆的大小 / 吃香蕉的速度) + (堆的大小 % 吃香蕉的速度 > 0)(这里的除法是int型除法)

import math
def minEatingSpeed(nums,t):
    
    if len(nums)==1:
        return (nums[0]+t-1)/t 
    #nums=sorted(nums)
    l,r = 1,max(nums)
    while l<r:
        mid = l + (r-l)//2
        cnt=0
        for p in nums:
            #cnt+= (p+mid-1)//mid #向上取整
            cnt += math.ceil(p/mid)
        if cnt > t:
            l = mid+1
        else:
            r = mid
    return l

piles =[ 30,11,23,4,20]
H = 5
minEatingSpeed(piles,H)
def minEatingSpeed( piles, H):
        maxv = max(piles)
        l = 1
        r = maxv
        while l < r:
            m = (r - l) // 2 + l
            cnt = 0
            for p in piles:
                cnt += (p + m - 1) // m
            if cnt > H:
                l = m + 1
            else:
                r = m
        return r

piles = [30,11,23,4,20]
H = 6
minEatingSpeed(piles,H)

1011.在D天内送达包裹的能力(中等)

传送带上的包裹必须在 D 天内从一个港口运送到另一个港口。

传送带上的第 i 个包裹的重量为 weights[i]。每一天,我们都会按给出重量的顺序往传送带上装载包裹。我们装载的重量不会超过船的最大运载重量。

返回能在 D 天内将传送带上的所有包裹送达的船的最低运载能力。

示例 1:

输入:weights = [1,2,3,4,5,6,7,8,9,10], D = 5

输出:15

解释:

船舶最低载重 15 就能够在 5 天内送达所有包裹,如下所示:

第 1 天:1, 2, 3, 4, 5

第 2 天:6, 7

第 3 天:8

第 4 天:9

第 5 天:10

请注意,货物必须按照给定的顺序装运,因此使用载重能力为 14 的船舶并将包装分成 (2, 3, 4, 5), (1, 6, 7), (8), (9), (10) 是不允许的。

示例 2:

输入:weights = [3,2,2,4,1,4], D = 3

输出:6

解释:

船舶最低载重 6 就能够在 3 天内送达所有包裹,如下所示:

第 1 天:3, 2

第 2 天:2, 4

第 3 天:1, 4

示例 3:

输入:weights = [1,2,3,1,1], D = 4

输出:3

解释:

第 1 天:1

第 2 天:2

第 3 天:3

第 4 天:1, 1

提示:

1 <= D <= weights.length <= 50000

1 <= weights[i] <= 500


def shipWithinDays(nums,t):
    
    if len(nums)==1:
        return (nums[0]+t-1)/t 
    #nums=sorted(nums)
    r = sum(nums)
    l = max(r//t, max(nums))
    
    while l<r:
        mid = l + (r-l)//2
        cnt=0
        cur =0
        for p in nums:
            if cur+p>mid:
                cur =0
                cnt += 1
            cur += p
        if cnt > t:
            l = mid+1
        else:
            r = mid
    return l

weights = [1,2,3,1,1] 
D = 4
shipWithinDays(weights,D)
class Solution:
    def shipWithinDays(self, weights, D):
        r = sum(weights)
        l = max(r//D, max(weights))
        
        while l <= r:
            m, need, cur = (l + r)//2, 1, 0
            for w in weights:
                if cur + w > m:
                    need += 1
                    cur = 0
                cur += w
            if need > D:
                l = m + 1
            else:
                r = m - 1
        return l
weights = [1,2,3,1,1] 
D = 4
s=Solution()
s.shipWithinDays(weights,D)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值