python二分查找=及=二分边界查找

二分查找python


什么是查找?

查找是在一组数据中寻找特定元素的过程。它在计算机科学和实际编程中扮演着重要的角色,因为我们经常需要从大量数据中快速找到所需的信息。


什么是二分查找?

在这里插入图片描述


二分查找是一种高效的查找算法,特别适用于有序列表。它通过将查找范围逐渐缩小一半来快速定位目标元素,从而大大减少了查找的时间复杂度。

时间复杂度对于二分查找算法来说,其时间复杂度为 O(log n),其中 n 表示数据集中元素的数量。

这是因为在每一步迭代中,二分查找都将查找范围缩小一半。假设初始数据集中有 n 个元素,经过一次迭代后剩下 n/2 个元素,再经过一次迭代后剩下 n/4 个元素,以此类推。经过 k 次迭代后,剩下的元素个数将是 n/(2^k)。

当剩下的元素个数变为 1 时,查找结束。即 n/(2^k) = 1,解这个方程可以得到 k = log2(n),因此算法最多需要 log2(n) 步来找到目标元素。这就是为什么二分查找的时间复杂度是 O(log n)。

需要注意的是,这里的 log 是以 2 为底的对数。这种时间复杂度的特点是,随着数据量的增加,算法的性能仍然能够保持较高的效率,相比于线性查找等时间复杂度较高的算法,二分查找在大规模数据集上表现更优秀。


二分查找的前提

二分查找的前提是数据必须有序。这可以是升序或降序,但必须是有序的,以便能够有效地将查找范围缩小。

二分查找的图解

在这里插入图片描述


二分查找的代码实现


如何获取中间值

中间值的获取一般有两种方法:

  1. mid = (left + right)// 2
    这种方式是将左边界 left 和右边界right的和除以 2 来计算中间位置。这种方式的优点是简单明了,直观易懂。但是在特别大的数据范围下,left + right 可能会导致整数溢出问题,因此在实际使用时需要注意。
  2. mid = left + (right - left) // 2
    这种方式是将右边界 right 减去左边界 left 的差再除以 2,然后加上左边界 left,从而计算中间位置。这种方式避免了整数溢出的问题,因为它是在较小的数值范围内进行运算。这种方式更安全,尤其在处理大范围的数据时更为可靠。

递归版

二分查找可以用递归实现。递归版本的代码会更简洁,但需要注意递归深度可能会影响性能。

def binary_search(nums,tagret,l ,r ):
    '''
    递归版的二分查找   # 大前提 :nums必须是有序数组
    :param nums: 带查找的数组
    :param tagret:  带查找的目标值
    :param l: 左指针
    :param r: 右指针
    :return: 查找到的索引  没找到  返回-1
    '''
    if l>r:
        return -1 # 表示不知道
    mid = l + (r - l) // 2
    if nums[mid] == tagret:
        return  mid
    elif nums[mid] > tagret:
        return binary_search(nums,tagret,l,mid - 1)
    else:
        return binary_search(nums, tagret, mid + 1, r)

nums = [1,2,2,2,2,2,3,4,5,6,7,8,9,10]

print(binary_search(nums,2,0,len(nums)-1))

循环版

def binary_search(nums,tagret,l ,r ):
    '''
    :param nums: 查找的序列
    :param tagret:  带查找值
    :param l:  查找的左边界
    :param r:   查找的右边界
    :return: 返回元素所在的索引位置  否则 返回-1
    '''
    while l <= r:
        mid = l + (r - l) // 2
        if nums[mid] == tagret:
            return  mid
        elif nums[mid] > tagret:
            r = mid - 1
        else:
            l = mid + 1
    return -1

nums = [1, 2, 2, 2, 2, 2, 3, 4, 5, 6, 7, 8, 9, 10]

print(binary_search(nums, 2, 0, len(nums) - 1))

二分查找进阶

如何查找多个tagret的边界索引?
在这里插入图片描述


边界值的特点如下:
在这里插入图片描述


左侧二分查找

方法1

def searchLeft(nums, target):
            # 左边界需要满足两个条件:
            # 1. 他的值为target
            # 2. 他的左边元素小于他,或者他的下标为0
            left, right = 0, len(nums)-1
            while left <= right:
                mid = (right-left)//2 + left
                if nums[mid] == target:
                    if mid == 0 or nums[mid-1] < target:
                        return mid
                    else:
                        right = mid - 1
                elif nums[mid] > target:
                    right = mid - 1
                else:
                    left = mid + 1
            return -1

作者:Qi_654321
链接:https://leetcode.cn/problems/find-first-and-last-position-of-element-in-sorted-array/solutions/2362521/liang-ci-er-fen-fa-cha-zhao-zuo-you-bian-hwhz/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

方法2:

def findleft(nums,tagret):
    l = 0
    r = len(nums)-1
    while l <= r:
        mid = l + (r - l) // 2
        if nums[mid] < tagret:
            l = mid + 1
        else:                    # nums[mid] >= tagret
            r = mid - 1
    return l
nums = [3,3,3,3,3,3,4,5,6]
print(findleft(nums,3))

右侧二分查找

方法1:

该方法简介明了,可读性较强!

def searchRight(nums, target):
            # 右边界需要满足两个条件:
            # 1. 他的值为target
            # 2. 他的右边元素大于他,或者他的下标为len(nums)-1
            left, right = 0, len(nums)-1
            while left <= right:
                mid = (right-left)//2 + left
                if nums[mid] == target:
                    if mid == len(nums)-1 or nums[mid+1] > target:
                        return mid
                    else:
                        left = mid + 1
                elif nums[mid] > target:
                    right = mid - 1
                else:
                    left = mid + 1
            return -1

作者:Qi_654321
链接:https://leetcode.cn/problems/find-first-and-last-position-of-element-in-sorted-array/solutions/2362521/liang-ci-er-fen-fa-cha-zhao-zuo-you-bian-hwhz/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

方法2:

def findright(nums,tagret):
    l = 0
    r = len(nums)-1
    while l <= r:
        mid = l + (r - l) // 2
        if nums[mid] <= tagret:
            l = mid + 1
        else:                    # nums[mid] >= tagret
            r = mid - 1
    return r
nums = [3,3,3,3,3,3,4,5,6]
print(findright(nums,3))

方法2 的弊端

无法查找不在序列中的元素索引

但是当程序需要同时得到左端点和右端点的索引值的时候,同时调用两个函数,最终的结果必定会左端点>右端点

'''
@Time    : 2023-08-26 11:37
@Author  : TAGRENLA
@File    : 直接查左右的二分.py
'''

def findleft(nums,tagret):
    l = 0
    r = len(nums)-1
    while l <= r:
        mid = l + (r - l) // 2
        if nums[mid] < tagret:
            l = mid + 1
        else:                    # nums[mid] >= tagret
            r = mid - 1
    return l

def findright(nums,tagret):
    l = 0
    r = len(nums)-1
    while l <= r:
        mid = l + (r - l) // 2
        if nums[mid] <= tagret:
            l = mid + 1
        else:                    # nums[mid] >= tagret
            r = mid - 1
    return r


nums = [11,22,33,44,55,66,77,88]
tagret = 12
print(findleft(nums,tagret=tagret))
print(findright(nums,tagret=tagret))
print(findleft(nums,tagret=tagret) > findright(nums,tagret=tagret))

1
0
True


本文创作来源

本文创作来源:力扣34:在排序数组中查找元素的第一个和最后一个位置

# 博主解题源代码
class Solution:
    def searchRange(self,nums, target):
        def findLeft(nums, target):
            left, right = 0, len(nums) - 1
            while left <= right:
                mid = left + (right - left) // 2
                if nums[mid] < target:
                    left = mid + 1
                else:  # nums[mid] >= tagret:
                    right = mid - 1
            return left
        
        def findRight(nums, target):
            left, right = 0, len(nums) - 1
            while left <= right:
                mid = left + (right - left) // 2
                if nums[mid] <= target:
                    left = mid + 1
                else:  # nums[mid] >= tagret:
                    right = mid - 1
            return right
        
        left = findLeft(nums, target)
        right = findRight(nums, target)
        
        if left <= right:
            return [left, right]
        else:
            return [-1, -1]
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

TAGRENLA

您的打赏是我创作的动力,谢谢!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值