二分查找精炼回顾-kevin

二分搜索回顾,由于习惯问题, 二分搜索问题都采用闭区间来思考 二分搜索总共就三类问题,统一规范如下, 由于都统一采用闭区间来思考,所以while的判定条件都是**left <= right ** mid 在都是在while循环之后再更新

left = 0, right = len(nums) - 1
#所以二分搜索区间是[left, right]

1,寻找一个数

def binarySearch(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
        elif nums[mid] > target:
            # 注意
            right = mid - 1
  • 由于采用闭区间,[left, right]无意义的条件是是left = right + 1,所以while的判定条件是left <= right 时候进入循环,跳出循环的条件就是left = right + 1

  • 由于采用都是闭区间,也就是发现mid 不是要查找的目标时候,由于闭区间应该更新为[left, right - 1] 或者更新为 [mid + 1, right] ,只要更新一定有1

  • 更新自己总结的小口诀是, ‘大右,小左’ 解读是中间值比目标大,更新右侧边界 , 中间值比目标小更新左侧边界如果nums[mid] > target更新left = mid + 1

    如果nums[mid] < target 更新 right = mid - 1

找边界问题的关键点在于,对nums[mid] == target这种情况的处理, 总结起来就是要找左边界,就是在[left, mid - 1] 中持续收缩,直到找到左边界,也就是>=这种 要找右边界,就是在[mid + 1, right] 中持续收缩,直到找到有边界。 所以当碰到目标值的时候,两者操作不同,获得边界自然就不同

2,寻找左侧边界

找左侧边界的特殊情况,如果 target 不存在,搜索左侧边界的二分搜索返回的索引是大于 target 的最小索引

可见,找到 target 时不要立即返回,而是缩小「搜索区间」的上界 right,在区间 [left, mid - 1] 中继续搜索,即不断向左收缩,达到锁定左侧边界的目的。

if nums[mid] < target:
    # 搜索区间变为 [mid+1, right]
    left = mid + 1
elif nums[mid] > target:
    # 搜索区间变为 [left, mid-1]
    right = mid - 1
elif nums[mid] == target:
    # 收缩右侧边界
    right = mid - 1

3,寻找右侧边界

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

总结

只要区间定义为闭区间,二分查找的判定条件就是 while left <= right ,其次,找边界问题与找点问题,不同点只在于num[mid] == target时候处理情况的不同。
用二分查找求最长递增子序列问题

class Solution:
    def lengthOfLIS(self, nums):
        top = [0] * len(nums)
        # 牌堆数初始化为 0
        piles = 0
        for i in range(len(nums)):
            # 要处理的扑克牌
            poker = nums[i]

            # 搜索左侧边界的二分查找(闭区间)
            left, right = 0, piles - 1#这个-1 经常遗漏。
            while left <= right:  # 注意这里是 <= 表示闭区间
                mid = (left + right) // 2
                if top[mid] > poker:
                    right = mid - 1  # 继续在左半部分搜索
                elif top[mid] < poker:
                    left = mid + 1  # 继续在右半部分搜索
                else:
                    right = mid - 1  # 继续向左搜索

            # 没找到合适的牌堆,新建一堆
            if left == piles:
                piles += 1
            # 把这张牌放到牌堆顶
            top[left] = poker

        # 牌堆数就是 LIS 长度
        return piles

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值