数据结构与算法之二分查找

124 阅读3分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 11 天,点击查看活动详情

作者: 千石
支持:点赞、收藏、评论
欢迎各位在评论区交流

前言

本文内容来自我平时学习的一些积累,如有错误,还请指正

在题目实战部分,我将代码实现和代码解释设置在了解题思路的下方,方便各位作为参考刷题

一些话

本文内容来自我平时学习的一些积累,如有错误,还请指正

在题目实战部分,我将代码实现和代码解释设置在了解题思路的下方,方便各位作为参考刷题

题目练习步骤:

  1. 给自己10分钟,读题并思考解题思路
  2. 有了思路以后开始写代码,如果在上一步骤中没有思路则停止思考并且看该题题解
  3. 在看懂题解(暂时没看懂也没关系)的思路后,背诵默写题解,直至能熟练写出来
  4. 隔一段时间,再次尝试写这道题目

前置知识

二分查找的前提

  • 目标函数单调性(单调递增或者递减)
  • 存在上下界
  • 能够通过索引访问

代码模板

left, right = 0, len(array)-1
while left <= right:
    mid = (left + right)/2
    if array[mid]==target:
        # find the target
        break or return result
    elif array[mid]<targ:
        left=mid+1
    else:
        right = mid -1

Eg:在递增数组里,查找31

 10,14,19,26,27,31,33,35,42,44

题目

69. x 的平方根

image.png

思路一:二分查找法

对于一个非负整数 x,从 0 到 x/2+1 中查找算术平方根。每次查找平方根的结果 res,通过比较 res * res 和 x 的大小关系来更新查找的范围。如果 res * res 等于 x,则直接返回 res,否则如果 res * res 小于 x,说明算术平方根在 res 的右边,更新查找的左边界,反之更新查找的右边界。

class Solution:
    def mySqrt(self, x: int) -> int:
        if x == 0:
            return 0
        left, right = 1, x // 2 + 1
        while left < right:
            mid = (left + right + 1) // 2
            if mid * mid > x:
                right = mid - 1
            else:
                left = mid
        return left

复杂度分析:

- 时间复杂度:O(log(x)),二分查找的时间复杂度。

- 空间复杂度:O(1),仅需要常数空间。
image.png

思路二:牛顿迭代法

牛顿迭代法是一种数值解法,通过迭代的方式求出函数的零点。在求算术平方根的问题中,使用牛顿迭代法,先随意猜测一个平方根的值,然后使用公式:res = (res + x/res) / 2 来迭代求解算术平方根的值。

class Solution:
    def mySqrt(self, x: int) -> int:
        if x == 0:
            return 0
        res = x
        while res * res > x:
            res = (res + x / res) // 2
        return int(res)

复杂度分析:

- 时间复杂度:O(log(x)),通过每次迭代,误差的大小会接近于原来的一半,因此总的迭代次数是 log(x) 级别的。

- 空间复杂度:O(1),仅需要常数空间。
image.png

33. 搜索旋转排序数组

image.png

思路:二分查找

  • 对于有序数组nums,使用二分查找,通过比较中间值与目标值的大小关系,将查询范围缩小为一半,并在其中继续查找目标值。
  • 对于旋转后的数组,通过比较nums[start]与nums[mid]的大小关系,可以判断出前半部分是否有序,并缩小查找范围。
  • 如果nums[start]<=nums[mid],说明前半部分有序,此时可以通过比较target与nums[mid]的大小关系缩小查找范围。
  • 如果nums[start]>nums[mid],说明前半部分无序,此时可以通过比较target与nums[start]的大小关系缩小查找范围。
class Solution:
    def search(self, nums: List[int], target: int) -> int:
        start, end = 0, len(nums) - 1
        while start <= end:
            mid = (start + end) // 2
            if nums[mid] == target:
                return mid
            if nums[start] <= nums[mid]:
                if target >= nums[start] and target < nums[mid]:
                    end = mid - 1
                else:
                    start = mid + 1
            else:
                if target <= nums[end] and target > nums[mid]:
                    start = mid + 1
                else:
                    end = mid - 1
        return -1
image.png