leetcode-二分查找

目录

leetcode-二分查找

原理

代码实现

代码模板

典型例题分解

410

33

1552

540

总结


leetcode-二分查找

原理

二分查找也叫折半查找。

基本思想:

1 在有序表中,取中间记录作为比较对象

1.1 若给定与中间记录的关键字相等,则查找成功;

1.2 若给定值小于中间记录的关键字,则在中间记录的左半区继续查找;

1.3 若给定值大于中间记录的关键字,则在中间记录的右半区继续查找。

2 不断重复1过程,直到查找成功,或在查找区域无记录,查找失败为止。

特点

  • 数据结构需要排好序(如单调递增,单调递减)

  • 数据结构可随机访问(如数组)

  • 根据中间元素可推测两侧元素性质(可缩小问题规模)

  • 元素多数为整型

代码实现

基于原理场景-在有序数组中查找是否存在值为key的元素。实现代码如下。

def binary_search(nums: list, key: int) -> bool:
    left = 0
    right = len(nums) - 1
    # <= 而不是 < ,当nums只有一个元素的时候不会进入
    while left <= right:
        mid = (left + right) >> 1
        value = nums[mid]
​
        if value == key:
            return True
        if value < key:
            left = mid + 1
        else:
            right = mid - 1
​
    return False

代码模板

def binary_search(nums: list, key: int) -> bool:
    left = xxx1
    right = xxx2
​
    while left <= right:
        mid = (left + right) >> 1
        value = nums[mid]
​
        if yyy1:
            return True
        if yyy2:# value < key
            left = mid + 1
        else:
            right = mid - 1
​
    return False

确定查找边界xxx1/xxx2

确定查找的终止条件yyy1/yyy2

 

典型例题分解

410

https://leetcode-cn.com/problems/split-array-largest-sum/

难点:如何将解题思路转化到使用二分查找。

解题思路(二分)

此题转化为二分的思路如下:

在[max(nums),sum(nums)]中找到一个值value,满足将nums切分成m个非空连续子数组,每个连续子数组的和小于等于value。

关键点:

  • 找区间内找值,二分查找

  • 数组切分,统计每个子数组值并判断小于某值。

代码实现

class Solution:
​
    def splitArray(self, nums: List[int], m: int) -> int:
        # 判断查找值是否满足
        def f(x: int) -> bool:
            total = 0
            nums_spilt = 1
            for num in nums:
                if total + num <= x:
                    total += num
                else:
                    nums_spilt += 1
                    total = num
                if nums_spilt > m:
                    return False
            return True
        # 二分查找
        left = max(nums)
        right = sum(nums)
        while left <= right:
            mid = (left + right) // 2
            if not f(mid):
                left = mid + 1
            else:
                right = mid - 1
        return left

33

https://leetcode-cn.com/problems/search-in-rotated-sorted-array/

题意

有一个旋转后的数组nums,问nums是否存在值为target的元素。

nums数组:

[nums[k], nums[k+1], ..., nums[n-1], nums[0], nums[1], ..., nums[k-1]]

解题思路(二分)

查找nums是否存在target元素(二分查找变体)

 

本体主要难点是在,怎么在一个经过旋转之后的数组中进行二分查找。

按照二分查找原理,当mid索引处元素值与target值不相等时,我们应该向左缩小搜索范围还是向右缩小搜索范围呢?

引用官方解答:

判断以mid作为分割符左右两子数组,哪边有序。判断target是否在有序数组范围内,在则指针往有序数组移,否则往相反方向移动。

代码实现

from typing import List
​
​
class Solution:
    def search(self, nums: List[int], target: int) -> int:
        left = 0
        right = len(nums) - 1
        while left <= right:
            mid = (left + right) >> 1
            value = nums[mid]
            if value == target:
                # return check(mid)
                return mid
            if nums[0] <= value:
                if nums[0] <= target < nums[mid]:
                    right = mid - 1
                else:
                    left = mid + 1
            else:
                if nums[mid] < target <= nums[len(nums)-1]:
                    left = mid + 1
                else:
                    right = mid - 1
​
        return -1
​
​
if __name__ == '__main__':
    print(Solution().search([4, 5, 6, 7, 0, 1, 2], 0))
    print(Solution().search([1, 3], 3))
    print(Solution().search([1, 3, 5], 3))
    print(Solution().search([1, 3, 5], 1))
    print(Solution().search([3, 1], 1))
​

1552

https://leetcode-cn.com/problems/magnetic-force-between-two-balls/solution/liang-qiu-zhi-jian-de-ci-li-by-leetcode-solution/

与410题类似。

区别在于

 

540

https://leetcode-cn.com/problems/single-element-in-a-sorted-array/

解题思路(二分)

二分查找典型题。

问题为,出现一次的元素在左半区间还是右半区间。

唯有一个数只会出现一次 -- 数组元素为奇数,该与左右两边数不相等。

如果mid处元素与左右两边不相等,则返回该元素值。

如果mid处元素与左右两边元素一者相等,则存在以下四种情况

  • mid处元素与左边元素相等

    • mid处右边数组元素个数为奇数,则指针右移1位

    • mid处右边数组元素个数为偶数,则指针左移2位

  • mid处元素与右边元素相等

    • mid处右边数组元素个数为奇数,则指针左移1位

    • mid处右边数组元素个数为偶数,则指针右移2位

代码实现

from typing import List
​
​
class Solution:
    def singleNonDuplicate(self, nums: List[int]) -> int:
        left = 0
        right = len(nums) - 1
        while left <= right:
            mid = (left + right) >> 1
            is_odd_right = (right - mid) % 2 == 1
            if nums[mid] == nums[mid - 1]:
                if is_odd_right:
                    left = mid + 1
                else:
                    right = mid - 2
            elif nums[mid] == nums[mid + 1]:
                if is_odd_right:
                    right = mid - 1
                else:
                    left = mid + 2
            else:
                return nums[mid]
​
​
if __name__ == '__main__':
    print(Solution().singleNonDuplicate([1, 1, 2, 3, 3, 4, 4, 8, 8]))
    print(Solution().singleNonDuplicate([3, 3, 7, 7, 10, 11, 11]))
​

 

总结

二分查找算法不难,难的是如何将题目的解题思路牵引至二分查找上。

基于以下,可考虑是否可以使用二分查找

  • 关键字:排序、搜索

  • 模式识别:有序或部分有序,基本使用二分搜索及其变种

  • 算法描述:“丢弃”一半的数据

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值