leetcode刷题——二分查找

  • 二分法是经典的查找算法,时间复杂度是O(logn),其使用的前提是有序数组和无重复元素(有重复元素的话返回的结果不唯一)。
  • 查找过程中,遵循循环一致性原则,各个环节都是保持一致的。初始化条件,决定搜索区间、while终止点、更新、返回
  • 二分查找的写法有两种:[left,right]和[left,right)
  • 二分查找的类型有三种:有序不重复找一个数、有序重复找第一次出现的索引位置、有序重复找最后一次出现的索引位置

一、题型

(一)找一个数

因为我们初始化 right = nums.length - 1 
所以决定了我们的「搜索区间」是 [left, right] 
终止条件是left==right+1
所以决定了 while (left <= right) 
同时也决定了 left = mid+1 和 right = mid-1 
因为我们只需找到⼀个 target 的索引即可 
所以当 nums[mid] == target 时可以⽴即返回

(二)寻找左侧边界的⼆分查找

因为我们初始化 right = nums.length
所以决定了我们的「搜索区间」是 [left, right) 
终止条件是left==right
所以决定了 while (left < right) 
同时也决定了 left = mid + 1 和 right = mid 
因为我们需找到 target 的最左侧索引 
所以当 nums[mid] == target 时不要⽴即返回 ⽽要收紧右侧边界以锁定左侧边界:right=mid
最后return left

例题:

35. 搜索插入位置

class Solution:
    def searchInsert(self, nums: List[int], target: int) -> int:
        # 第一种写法:[left,right]
        left=0
        right=len(nums)-1
        while left<=right:   # 两端闭
            mid=left+(right-left)//2
            if nums[mid]==target:
                right=mid-1  # 收缩右边界
            elif nums[mid]<target:
                left=mid+1
            else:right=mid-1
        return left
        # 第二种写法:[left,right)
        left=0
        right=len(nums)
        while left<right:   # 两端闭
            mid=left+(right-left)//2
            if nums[mid]==target:
                right=mid  # 收缩右边界
            elif nums[mid]<target:
                left=mid+1
            else:
                right=mid
        return left

69. Sqrt(x)

  • 易错点:(1)在于初始边界尽量取大一些,不然会遇到0、1、2不符合的情况;(2)最后的返回值:[left,right)终止条件是left=right,所以返回right-1;[left,right}终止条件是left=right+1,所以返回right;
class Solution:
    def mySqrt(self, x: int) -> int:
    # [left,right),注意right=x//2+1
        if x==0 or x==1:return x
        else:
            left,right=1,x//2+1  # 查找范围是[1,x//2],区间是左闭右闭
            ans=0
            while left<right:
                mid=left+(right-left)//2
                if mid<x/mid:        #  在 mid 很大的时候,mid * mid 有可能会整型溢出
                    left=mid+1       
                elif mid==x/mid:
                    return mid
                else:
                    right=mid
            return right-1   # 因为终止条件是left=right+1,然后要找的是取整的结果,所以是取小


#  [left,right]
class Solution:
    def mySqrt(self, x: int) -> int:
        if x==0 or x==1:return x
        else:
            left,right=1,x//2  # 查找范围是[1,x//2],区间是左闭右闭
            ans=0
            while left<=right:
                mid=left+(right-left)//2
                if mid<x/mid:        #  在 mid 很大的时候,mid * mid 有可能会整型溢出
                    left=mid+1       
                elif mid==x/mid:
                    return mid
                else:
                    right=mid-1
            return right   # 因为终止条件是left=right+1,然后要找的是取整的结果,所以是取小

(三)寻找右侧边界的⼆分查找

因为我们初始化 right = nums.length 
所以决定了我们的「搜索区间」是 [left, right) 
所以决定了 while (left < right) 
终止条件是left==right
同时也决定了 left = mid + 1 和 right = mid 
因为我们需找到 target 的最右侧索引 
所以当 nums[mid] == target 时不要⽴即返回 ⽽要收紧左侧边界以锁定右侧边界:left=mid+1
⼜因为收紧左侧边界时必须 left = mid + 1 
所以最后⽆论返回 left 还是 right,必须减⼀:因为left因为收紧边界多加了1return left-1\right-1

例题:

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

class Solution:
    def searchRange(self, nums: List[int], target: int) -> List[int]:
        res=[]
        # 先找开始位置
        left,right=0,len(nums)  # [left,right)
        while left < right:
            mid=left+(right-left)//2
            if nums[mid]==target:
                right=mid
            elif nums[mid]>target:
                right=mid
            elif nums[mid]<target:
                left=mid+1
        if left>=len(nums) or nums[left]!=target:res.append(-1)
        else:res.append(left)

        # 再找结束位置
        left,right=0,len(nums)
        while left < right:
            mid=left+(right-left)//2
            if nums[mid]==target:
                left=mid+1
            elif nums[mid]>target:
                right=mid
            elif nums[mid]<target:
                left=mid+1
        if right<=0 or nums[right-1]!=target:res.append(-1)
        else:res.append(right-1)
        
        return res

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值