【算法笔记】二分查找

二分查找

第一题 纯正二分查找

给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1。

来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/binary-search

python 知识复习

# 关于范围复制
for i in range(100) 是指099,不包括最后一个100

# 关于创建长度固定的列表
nums = [0] * 100
nums = [None] * 100
nums = [0 for i in range(100)]
nums = [[0 for i in rnage(100)] for j in range(100)]

有关边界的思考

这一题我认为最关键的问题是边界问题,大体的算法逻辑是很简单的:

  1. 设定两个潜在范围边界,初始为最大边界(len(nums)-1)和最小边界0
  2. 通过不断比较(循环)中间值(二分)和目标值(target)的大小,改变边界,收缩范围
  3. 可能的结果:①通过不断收缩找到目标值 nums[mid]②边界收紧,未找到目标值

这其中比较讲究的点有

  1. 中间值的取法:存在范围中有偶数个个体和奇数个个体的不同情况,奇数个个体的情况下不存在问题,因为有实际的中间量。问题是偶数个个体下,是不存在具体的中间值的,势必要取前一个或者后一个。而因为存在target在初始边界的情况,这两种情况容易导致无法在循环内找到目标值,因此需要跳出循环单独做善后工作
# 取前一个
mid = (min + max) // 2
# 取后一个
mid = (mid + max) // 2 + 1
  1. 因此,需要【mid取法】【终止判定】【善后工作】三者互相配合来处理所有情况

构想解法

我们以如下配合为例

  1. mid取法:mid = (min + max) // 2
  2. 由于以上情况在会最终收缩在min = mid < max的情况,这时候目标值要么在min 和 max中间(min < mid = max同理),要么不存在,所以做一下善后工作:
while 1
	# 正常二分循环 mid = (min + max)  // 2
    # 善后
    if mid == min: # 范围已经收缩到最小 
        if nums[min] == target:
            return min
        elif nums[max] == target:
            return max
        else:
            return -1   

最终解法

class Solution(object):
    def search(self, nums, target):
        """
        :type nums: List[int]
        :type target: int
        :rtype: int
        """
        min = 0
        max = len(nums) -1
        mid = max // 2
        while 1:
            if nums[mid]>target:
                max = mid
            elif nums[mid]<target:
                min = mid
            else:
                return mid
            mid=(min+max)//2

            if mid == min: # 范围已经收缩到最小 
                if nums[min] == target:
                    return min
                elif nums[max] == target:
                    return max
                else:
                    return -1   

时间复杂度O(logn)

执行用时:20 ms, 在所有 Python 提交中击败了91.16%的用户

内存消耗:13.9 MB, 在所有 Python 提交中击败了61.90%的用户

第二题 二分的应用 平方根

给你一个非负整数 x ,计算并返回 x 的 算术平方根 。

由于返回类型是整数,结果只保留 整数部分 ,小数部分将被 舍去 。

注意:不允许使用任何内置指数函数和算符,例如 pow(x, 0.5) 或者 x ** 0.5

来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/sqrtx

构想解法

最开始简单最直接的思路当然是暴力遍历,这种O(n)的算法自然在时间表现上很不满意

 sta = 0
        while (sta * sta) <= x:
            sta = sta + 1
        return sta - 1

执行用时: 964 ms

内存消耗: 13 MB

后来想了一些小方法,也是指标不治本

 		n = (len(str(x)) +1)//2
        sta = 1 	
        for i in range(n-1):
            sta =sta *10
            
        sta = sta -1   
        while sta * sta <=x:
            sta=sta+1       
        return sta-1

执行用时: 824 ms

内存消耗: 13.1 MB

但是既然

①有遍历的需求

②而且被遍历的目标是天然递增的

那么二分也是很自然的想法

最终解法

		low = 0
        high = x
        mid = (low + high)//2
        while mid > low:
            if mid*mid > x:
                high = mid
            elif mid*mid < x:
                low = mid
            else:
                return mid
            mid = (low + high) // 2
        return high if high*high == x else low

执行用时: 16 ms

内存消耗: 13.1 MB

总结,凡是在有序数组中寻找一个数的问题,都可以用二分查找来将复杂度从n降低到logn

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值