Task 04 数组⼆分查找

@创建于:20211122

1、二分法介绍

参考资料:二分查找知识

「二分查找算法(Binary Search Algorithm)」,也叫做 「折半查找算法」、「对数查找算法」。是一种在有序数组中查找某一特定元素的搜索算法。

基本算法思想:先确定待查找元素所在的区间范围,在逐步缩小范围,直到找到元素或找不到该元素为止。

二分法查找的思路如下:
(1)首先,从数组的中间元素开始搜索,如果该元素正好是目标元素,则搜索过程结束,否则执行下一步。
(2)如果目标元素大于/小于中间元素,则在数组大于/小于中间元素的那一半区域查找,然后重复步骤(1)的操作。
(3)如果某一步数组为空,则表示找不到目标元素。
二分法查找的时间复杂度O(logn)。

在这里插入图片描述

2、704. 二分查找

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
            elif nums[mid] < target:
                left = mid + 1
            else:
                right = mid - 1
        return -1

在这里插入图片描述

3、35. 搜索插入位置

class Solution:
    def searchInsert(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
            elif nums[mid] < target:
                left = mid + 1
            else:
                right = mid - 1
        return left

在这里插入图片描述

4、374. 猜数字大小

class Solution:
    def guessNumber(self, n: int) -> int:
        left = 1
        right = n

        # 注意是<号
        while left < right:
            mid = left + (right - left) // 2
            if guess(mid) <= 0:
                # 答案在区间 [left, mid] 中
                right = mid
            else:
                # 答案在区间 [mid+1, right] 中
                left = mid + 1

        # 此时有 left == right,区间缩为一个点,即为答案
        return left

在这里插入图片描述

5、69. Sqrt(x)

class Solution:
    def mySqrt(self, x: int) -> int:
        left = 0
        right = x
        res = -1

        while left <= right:
            mid = left + (right - left) // 2

            # 在[mid+1, right]中
            if mid*mid<=x:
                res = mid
                left = mid + 1
            # 在[left, mid-1]
            else:
                right = mid - 1
        return res

在这里插入图片描述

6、167. 两数之和 II - 输入有序数组

使用双指针

  • 初始时两个指针分别指向第一个元素位置和最后一个元素的位置。
  • 每次计算两个指针指向的两个元素之和,并和目标值比较。
  • 如果两个元素之和等于目标值,则发现了唯一解。
  • 如果两个元素之和小于目标值,则将左侧指针右移一位。
  • 如果两个元素之和大于目标值,则将右侧指针左移一位。
  • 移动指针之后,重复上述操作,直到找到答案。

https://leetcode-cn.com/problems/two-sum-ii-input-array-is-sorted/solution/liang-shu-zhi-he-ii-shu-ru-you-xu-shu-zu-by-leet-2/

在这里插入图片描述

class Solution:
    def twoSum(self, numbers: List[int], target: int) -> List[int]:
        left = 0
        right = len(numbers) - 1

        while left < right:
            total = numbers[left] + numbers[right]
            if total == target:
                return [left+1, right+1]
            elif total < target:
                left += 1
            else:
                right -= 1
        return [-1, -1]

7、1011. 在 D 天内送达包裹的能力

  • 这艘船最小的运载能力必须是所有货物中最重的那一件max(weights), 否则无法完成运输的任务。
  • 这艘船最大的运载能力就是一次性把所有的货都搬走, 就是sum(weights). 所以答案就在区间[max(weights), sum(weights)]之间。
class Solution:
    def shipWithinDays(self, weights: List[int], days: int) -> int:
        # 最小值得是任何一个货物都可以运走, 不可以分割货物
        left = max(weights)
        # 最大值是一趟就全部运走, 所以是所有货物之和
        right = sum(weights)

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

            # 计算船的最低运载能力为mid时,需要的天数
            need = 1
            cur = 0
            for weight in weights:
                if cur + weight > mid:
                    need += 1
                    cur = 0
                cur += weight
            
            # 如果天数超了, 说明运载能力有待提升, start改大一点, 继续二分搜索
            if need > days:
                left = mid + 1
            # 否则运载能力改小一点继续搜索
            else:
                right = mid
        
        return left

在这里插入图片描述

8、278. 第一个错误的版本

8.1 外层循环体 不取等号 情况解释:

循环结束条件是 left==right,因为本题应该默认一定有错误的版本,返回哪一个都可以,即直接返回 left或者right。但是在二分查找某个数值时,不写等号还要判断 left位置的元素是否等于目标数。

class Solution:
    def firstBadVersion(self, n):
        """
        :type n: int
        :rtype: int
        """
        left = 1
        right = n

        while left < right:
            mid = left + (right-left)//2
            wrong = isBadVersion(mid)
            if not wrong:
                left = mid + 1
            else:
                right = mid
        return left

在这里插入图片描述

8.2 外层循环体 取等号 情况解释:

利用二分查找求左边界(收缩右边界)

class Solution:
    def firstBadVersion(self, n):
        """
        :type n: int
        :rtype: int
        """
        left = 1
        right = n

        while left <= right:
            mid = left + (right-left)//2
            wrong = isBadVersion(mid)
            if not wrong:
                left = mid + 1
            else:
                right = mid - 1
        return left

在这里插入图片描述

9、33. 搜索旋转排序数组

二分法的变种,需要预先判断,哪个部分是有序部分。
在这里插入图片描述

class Solution:
    def search(self, nums: List[int], target: int) -> int:
        if not nums:
            return -1
        
        left, right = 0, len(nums)-1

        while left <= right:
            mid = left + (right-left)//2
            if nums[mid] == target:
                return mid
            
            if nums[0] <= nums[mid]:
                if nums[0] <= target and target < nums[mid]:
                    right = mid - 1
                else:
                    left = mid + 1
            else:
                if target > nums[mid] and target <= nums[-1]:
                    left = mid + 1
                else:
                    right = mid - 1
        return -1 

在这里插入图片描述

10、153. 寻找旋转排序数组中的最小值

参考链接
第一种情况是 nums[pivot] < nums[high]。如下图所示,这说明 nums[pivot]是最小值右侧的元素,因此我们可以忽略二分查找区间的右半部分。
在这里插入图片描述
第二种情况是 nums[pivot]>nums[high]。如下图所示,这说明 nums[pivot]是最小值左侧的元素,因此我们可以忽略二分查找区间的左半部分。
在这里插入图片描述

class Solution:
    def findMin(self, nums: List[int]) -> int:
        left, right = 0, len(nums)-1
        while left < right:
            mid = left + (right - left) // 2
            if nums[mid] < nums[right]:
                right = mid
            else:
                left = mid + 1
        return nums[left]
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值