必刷?704. 二分查找 以及两种变体

二分查找是一个简单但是热门的方法,二分的模板也非常多,这里提供一个简单好记、且不会索引越界的写法,并在此基础上改写得到一些变体,如查找最后一个目标元素,查找第一个大于(或者小于)目标的数。

包含的题目链接:
704. 二分查找:https://leetcode.cn/problems/binary-search/
705. 搜索插入位置:https://leetcode.cn/problems/search-insert-position/
34. 在排序数组中查找元素的第一个和最后一个位置:https://leetcode.cn/problems/find-first-and-last-position-of-element-in-sorted-array/

题目描述

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

2.非递归二分

二分模板,理解+记忆即可。

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:
            middle = left + (right - left) // 2
            if nums[middle] > target:
                right = middle - 1
            elif nums[middle] < target:
                left = middle + 1
            else:
                return middle
        return -1

3.递归二分

如果非要写个递归版本的,那也只能写一个。

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

        def binary_search(nums, begin, end, target):
            if begin > end:
                return -1
            middle = begin + ((end - begin)>>1)
            if nums[middle] > target:
                return binary_search(nums, begin, middle - 1, target)
            if nums[middle] < target:
                return binary_search(nums, middle + 1, end, target)
            return middle
        
        return binary_search(nums, 0, len(nums) - 1, target)

4.变体1:搜索插入位置

寻找第一个大于target的数,或者是最后一个target,可见35. 搜索插入位置
在普通的二分上稍作修改即可,当nums[middle] == target时,我们仍然认为没有找到,当作nums[middle] < target处理,这样left指针就会进一步向前,最后一个target一定在left - 1位置(前提是没有越界);如果找第一个大于target的位置,那就是left本身。

class Solution:
    def searchInsert(self, nums: List[int], target: int) -> int:
        if not nums:
            return -1
        left, right = 0, len(nums) - 1
        while left <= right:
            middle = left + ((right - left)>>1)
            if nums[middle] <= target:  # 等于也视作小于
                left = middle + 1
            else:
                right = middle - 1
        # target可能在left - 1位置
        if left - 1 >= 0 and nums[left - 1] == target:
            return left - 1

        return left  # 第一个大于target位置

4.变体2:在排序数组中查找元素的第一个和最后一个位置

链接:在排序数组中查找元素的第一个和最后一个位置
其实和变体1没什么区别,只不过现在还要找第一个target或者第一个小于target的位置。和上面一样,nums[middle] == target也认为nums[middle]>target,另right = middle - 1, 在不越界的前提下,第一个target在right + 1位置,第一个小于target在right位置。

class Solution:
    def searchRange(self, nums: List[int], target: int) -> List[int]:
        if not nums:
            return [-1, -1]
        first = self.binary_search(nums, target, pos = 'first')
        last = self.binary_search(nums, target, pos = 'last')
        return [first, last]
        
    def binary_search(self, nums, target, pos='first'):
        begin, end = 0, len(nums) - 1
        while begin <= end:
            middle = begin + (end - begin) // 2
            if pos == 'last':
                if nums[middle] <= target:
                    begin = middle + 1
                else:
                    end = middle - 1
            if pos == 'first':
                if nums[middle] >= target:
                    end = middle - 1
                else:
                    begin = middle + 1
        
        if pos == 'last':
            if begin - 1 >= 0 and nums[begin - 1] == target:
                return begin - 1
            return -1
        if pos == 'first':
            if end + 1 < len(nums) and nums[end + 1] == target:
                return end + 1
            return -1
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值