leetcode—22.二分查找题目leetcode总结

引言

  二分查找的问题定义为:

给定一个由数字组成的有序数组nums,并给你一个数字target。问nums 中是否存在target。如果存在,则返回其在nums 中的索引。如果不存在,则返回-1

常见的变体有:

  1. 如果存在多个满足条件的元素,返回最左边满足条件的索引。
  2. 如果存在多个满足条件的元素,返回最右边满足条件的索引。
  3. 数组不是整体有序的。比如先升序再降序,或者先降序再升序。
  4. 将一维数组变成二维数组。

我们要注意:二分查找的前提是数组必须是有序的。如果给定数组是无序的,在运用二分查找前需要先排序。

查找一个数

算法描述:

先从数组的中间元素开始,
如果中间元素正好是要查找的元素,则搜索过程结束;
如果目标元素大于中间元素,则在数组大于中间元素的那一半中查找,而且跟开始一样从中间元素开始比较。
如果目标元素小于中间元素,则在数组小于中间元素的那一半中查找,而且跟开始一样从中间元素开始比较。
如果在某一步骤数组为空,则代表找不到。

思维框架:

首先定义搜索区间为[left, right],注意是左右都闭合,之后会用到这个点。
由于定义的搜索区间为[left, right],因此当left <= right 的时候,搜索区间都不为空,此时我们都需要继续搜索。也就是说终止搜索条件应该为left <= right

  1. 循环体内,我们不断计算mid,并将nums[mid]与目标值比对。
  2. 如果nums[mid]等于目标值,则提前返回mid(只需要找到一个满足条件的即可)
    如果nums[mid]小于目标值,说明目标值在mid右侧,这个时候搜索区间可缩小为[mid + 1, right]
    如果nums[mid]大于目标值,说明目标值在mid左侧,这个时候搜索区间可缩小为[left,mid - 1]
  3. 循环结束都没有找到,则说明找不到,返回-1表示未找到

代码实现:

def binary_serach(lis,key):
    low = 0
    high = len(lis) - 1
    # 终止搜索条件是搜索区间为空,定义的搜索区间为闭合区间[low,high]
    # 如果low < high,则搜索区间我[low,high),此时不会判断low=high的那个元素
    while low <= high:
        mid = int(low +(high - low) / 2)
        if lis[mid] == key:
            return mid
        elif lis[mid] >= key:
            high = mid - 1
        elif lis[mid] <= key:
            low = mid + 1
    # 循环结束条件是搜索空间为空,即数组中不含有目标值
    return -1

if __name__ == '__main__':
    nums = [1, 3, 4, 5, 6, 8, 12, 14, 16]
    target = 8
    result = binary_serach(nums, target)
    print(result)

寻找最左边的满足条件的值

思维框架:

首先定义搜索区间为[left, right],注意是左右都闭合,之后会用到这个点。
由于定义的搜索区间为[left, right],因此当left <= right 的时候,搜索区间都不为空,此时我们都需要继续搜索。也就是说终止搜索条件应该为left <= right

  1. 循环体内,我们不断计算mid,并将nums[mid]与目标值比对。
  2. 如果nums[mid]等于目标值,则收缩右边界,我们找到了一个备胎,继续看看左边还有没有了
    如果nums[mid]小于目标值,说明目标值在mid右侧,这个时候搜索区间可缩小为[mid + 1, right]
    如果nums[mid]大于目标值,说明目标值在mid左侧,这个时候搜索区间可缩小为[left,mid - 1]
  3. 由于不会提前返回,因此我们需要检查最终的left,看 nums[left]是否等于target。
    如果不等于target,或者left出了右边边界了,说明至死都没有找到一个备胎,则返回-1.否则返回left 即可,备胎转正。
def binary_serach(lis, key):
    low, high = 0, len(lis) - 1
    while low <= high:
        mid = int(low + (high - low) / 2)
        # =与>这里可以合并
        if lis[mid] == key:
            # 收缩右边界
            high = mid - 1
        if lis[mid] > key:
            high = mid - 1
        if lis[mid] < key:
            low = mid + 1
    # 如果不等于key,或者left出了右边边界了
    if low >= len(lis) or lis[low] != key:
        return -1
    return low

if __name__ == '__main__':
    nums = [1, 2, 3, 5, 5, 5, 12, 14, 16]
    target = 5
    result = binary_serach(nums, target)
    print(result)
面试题 08.03. 魔术索引

在数组A[0…n-1]中,有所谓的魔术索引,满足条件A[i] = i。给定一个有序整数数组,编写一种方法找出魔术索引,若有的话,在数组A中找出一个魔术索引,如果没有,则返回-1。若有多个魔术索引,返回索引值最小的一个。

示例:

输入:nums = [0, 2, 3, 4, 5]
输出:0
说明: 0下标的元素为0

class Solution:
    def findMagicIndex(self, nums: List[int]) -> int:
        return self.find_magic_index(nums,0,len(nums)-1)
    def find_magic_index(self,nums,left,right):
        # 未找到符合要求的,返回-1
        if left > right:
            return -1
        # 中间元素
        mid = int(left + (right - left) / 2)
        # 看是否能够找到第一个魔术索引,先往左侧找
        left_ans = self.find_magic_index(nums,left,mid-1)
        # 如果左侧存在,那么返回left_ans
        if left_ans != -1:
            return left_ans
        # 如果左侧不存在,先比较nums[mid] == mid
        elif nums[mid] == mid:
            return mid
        # 如果左侧不存在,且nums[mid] != mid,往右侧找
        return self.find_magic_index(nums,mid+1,right)
  • 时间复杂度:最坏情况下会达到 O(n) 的时间复杂度,其中 n 为数组的长度。
  • 空间复杂度:递归函数的空间取决于调用的栈深度,而最坏情况下我们会递归 nn、 层,即栈深度为 O(n),因此空间复杂度最坏情况下为O(n)。

寻找最右边的满足条件的值

思维框架:

首先定义搜索区间为[left, right],注意是左右都闭合,之后会用到这个点。
由于定义的搜索区间为[left, right],因此当left <= right 的时候,搜索区间都不为空,此时我们都需要继续搜索。也就是说终止搜索条件应该为left <= right

  1. 循环体内,我们不断计算mid,并将nums[mid]与目标值比对。
  2. 如果nums[mid]等于目标值,则收缩左边界,我们找到了一个备胎,继续看看右边还有没有了
    如果nums[mid]小于目标值,说明目标值在mid右侧,这个时候搜索区间可缩小为[mid + 1, right]
    如果nums[mid]大于目标值,说明目标值在mid左侧,这个时候搜索区间可缩小为[left,mid - 1]
  3. 由于不会提前返回,因此我们需要检查最终的right,看 nums[right]是否等于target。
    如果不等于target,或者right出了左边边界了,说明至死都没有找到一个备胎,则返回-1.否则返回right即可,备胎转正。
def binary_serach(lis, key):
    low, high = 0, len(lis) - 1
    while low <= high:
        mid = int(low + (high - low) / 2)
        # =与<这里可以合并
        if lis[mid] == key:
            # 收缩左边界
            low = mid + 1
        
  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值