Leetcode刷题——二分查找

Leetcode刷题——二分查找

二分查找细节

第一个,最基本的二分查找算法:

1 因为我们初始化 right = nums.length - 1
2 所以决定了我们的「搜索区间」是 [left, right]
3 所以决定了 while (left <= right)
4 同时也决定了 left = mid+1 和 right = mid-1
5 
6 因为我们只需找到一个 target 的索引即可
7 所以当 nums[mid] == target 时可以立即返回

第二个,寻找左侧边界的二分查找:

1 因为我们初始化 right = nums.length
2 所以决定了我们的「搜索区间」是 [left, right)
3 所以决定了 while (left < right)
4 同时也决定了 left = mid + 1 和 right = mid
5 
6 因为我们需找到 target 的最左侧索引
7 所以当 nums[mid] == target 时不要立即返回
8 而要收紧右侧边界以锁定左侧边界

第三个,寻找右侧边界的二分查找:

 1 因为我们初始化 right = nums.length
 2 所以决定了我们的「搜索区间」是 [left, right)
 3 所以决定了 while (left < right)
 4 同时也决定了 left = mid + 1 和 right = mid
 5 
 6 因为我们需找到 target 的最右侧索引
 7 所以当 nums[mid] == target 时不要立即返回
 8 而要收紧左侧边界以锁定右侧边界
 9 
10 又因为收紧左侧边界时必须 left = mid + 1
11 所以最后无论返回 left 还是 right,必须减一

刷题

    1. ⼆分查找
class Solution:
    def search(self, nums: List[int], target: int) -> int:
        left = 0
        right = len(nums)-1
        ans = -1
        while left <= right:
            mid = (left+right)//2
            if nums[mid] == target:
                ans = mid
                left = mid + 1
                return ans
            if nums[mid] < target:
                left = mid + 1
            else:
                right = mid - 1
        return  ans
    1. 搜索插⼊位置
class Solution:
    def searchInsert(self, nums: List[int], target: int) -> int:
        left, right = 0, len(nums) - 1

        while left <= right:
            middle = (left + right) // 2

            if nums[middle] < target:
                left = middle + 1
            elif nums[middle] > target:
                right = middle - 1
            else:
                return middle
        return right + 1
    1. 猜数字⼤⼩
class Solution:
    def guessNumber(self, n: int) -> int:
        left, right = 1, n
        while left < right:
            mid = (left + right) // 2
            if guess(mid) <= 0:
                right = mid   # 答案在区间 [left, mid] 中
            else:
                left = mid + 1   # 答案在区间 [mid+1, right] 中
        
        # 此时有 left == right,区间缩为一个点,即为答案
        return left
    1. Sqrt(x)
      注意返回左边界值
class Solution:
    def mySqrt(self, x: int) -> int:
        left = 0
        right = x
        ans = -1
        while left <= right :
            mid  = (left + right)//2
            if mid * mid <= x:
                left  += 1
                ans = mid
            else:
                right = mid -1
        return ans

    1. 两数之和 II - 输⼊有序数组
      双指针,时间复杂度O(n)
class Solution:
    def twoSum(self, numbers: List[int], target: int) -> List[int]:
       left = 0
       right = len(numbers) - 1
       while left < right:
           if numbers[left] + numbers[right] == target:
               return left+1, right+1
           if numbers[left] + numbers[right] > target:
                right -= 1
           if numbers[left] + numbers[right] < target:
                left += 1
    1. 在 D 天内送达包裹的能⼒
class Solution:
    def shipWithinDays(self, weights: List[int], D: int) -> int:
        # 最小值得是任何一个货物都可以运走, 不可以分割货物
        start = max(weights)
        # 最大值是一趟就全部运走, 所以是所有货物之和
        end = sum(weights)
        # 二分法模板
        while start < end:
            # 先求中间值
            mid = (start + end)//2

            # 计算这个中间值需要计算需要多少天运完
            days = self.countDays(mid, weights)
            # 如果天数超了, 说明运载能力有待提升, start改大一点, 继续二分搜索
            if days > D:
                start = mid + 1
            # 否则运载能力改小一点继续搜索
            else:
                end = mid
        return start

    def countDays(self, targetWeight, weights):
        days = 1
        current = 0
        for weight in weights:
            current += weight
            if current > targetWeight:
                days += 1
                #当满足运载量,则运输一批,天数增加1,接着从超重货物开始计重
                current = weight
        return days
  • 0278 第一个错误的版本
    左闭右开区间
class Solution:
    def firstBadVersion(self, n):
        left = 1
        right = n
        while left < right:
            mid = (left + right) // 2
            if isBadVersion(mid):
                right = mid
            else:
                left = mid + 1
        return left
  • 0033 搜索旋转排序数组
class Solution:
    def search(self, nums: List[int], target: int) -> int:
        left = 0
        right = len(nums) - 1
        while left <= right:
            mid = left + (right - left) // 2
            if nums[mid] == target:
                return mid
            #确认target在旋转数组的哪一侧
            if nums[mid] >= nums[left]:
            #确认target所在区间,注意区分等号
                if nums[mid] > target and target >= nums[left]:
                    right = mid - 1
                else:
                    left = mid + 1
            else:
                if nums[mid] < target and target <= nums[right]:
                    left = mid + 1
                else:
                    right = mid - 1

        return -1
  • 0153 寻找旋转排序数组中的最小值
class Solution:
    def findMin(self, nums: List[int]) -> int:
        left = 0
        right = len(nums) - 1
        while left  < right :
            mid  = ( left + right )//2
            #mid 与左右极值对比
            if nums[mid]  > nums[right]:
                left = mid +1
            else:
                right = mid

        return nums[left]

参考资料:
算法通关手册 https://github.com/itcharge/LeetCode-Py
https://leetcode-cn.com/problems/capacity-to-ship-packages-within-d-days/solution/chi-xiao-dou-mei-ri-yi-ti-python-jian-mi-o01o/

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值