搜索--二分查找

二分查找,它是基于分治策略的高效搜索算法,利用数据的有序性,每轮缩小一半搜索范围,直至找到目标元素或搜索区间为空为止。
优势和局限:二分查找仅适用于有序数据的数组(需跳跃式(非连续地)访问元);其时间复杂度通常为 O ( l o g n ) O(\mathrm{log} n) O(logn)


场景1:给定一个数组 nums,其数组没有重复元素且元素按从小到大排序,数组长度为 n;在数组查找元素 target 并返回对应的下标,若数组不包含目标元素,就返回 − 1 -1 1
算法流程
(1)初始化两个指针 i=0j=n-1,分别指向数组的首元素和尾元素,代表搜索区间 [ 0 , n − 1 ] [0, n-1] [0,n1]。此时我们规定:下标严格小于 i 的元素一定小于 target,而下标严格大于 j 的元素一定大于 target
(2)满足 i <= j 的条件下,循环执行以下两步:
 (2.1)计算中点索引 m = ⌊ ( i + j ) / 2 ⌋ m = \lfloor(i+j)/2\rfloor m=⌊(i+j)/2
 (2.2)判断 nums[m]target 的大小关系,分为以下三种情况:
   a、当 nums[m] = target 时,说明找到 target,因此返回索引 m
   b、当 nums[m] < target 时,说明 target 在区间 [ m + 1 , j ] [m+1, j] [m+1,j] 中,因此执行 i = m + 1 i = m + 1 i=m+1
   c、当 nums[m] > target 时,说明 target 在区间 [ i , m − 1 ] [i, m-1] [i,m1] 中,因此执行 j = m − 1 j = m - 1 j=m1

在这个过程中,区间大小会不断减少,知道遇到目标元素或者为空(i > j),结束循环,查找结束。
情况 b 中,修改指针 i 的取值,说明区间 [ 0 , i − 1 ] [0, i-1] [0,i1] 的元素均小于 target(此时 i = m + 1 i=m+1 i=m+1),满足上述规定;
情况 c 中,修改指针 j 的取值,说明区间 [ j + 1 , n − 1 ] [j+1, n-1] [j+1,n1] 的元素均大于 target(此时 j = m − 1 j=m-1 j=m1),满足上述规定;
当出现 i = j 时,说明待判断的元素只剩下一个:若是目标值,则直接返回下标,若不是目标值,根据其与 target 关系修改指针的值,进而查找范围为空。

def binarySearch(nums, target):
    n = len(nums)
    i, j = 0, n-1
    while i <= j:
        m = i + (j-i)//2
        if nums[m] < target:
            i = m+1
        elif nums[m] > target:
            j = m-1
        else:
            return m
    return -1

场景2:在上述数组的基础上,假设数组中存在多个 target,则上述的二分查找只能返回其中一个的索引,无法确定该元素的左边和右边还有多少 target
为此,扩展上面的二分查找代码,使其能找到第一个 target,并返回对应的下标。

算法流程:整体流程保持不变,初始化 i=0j=n-1;接着,每轮循环中计算索引 m m m,再判断 nums[m]target 的关系:
(1) 当 nums[m] > targetnums[m] < target 时,说明还没有找到目标值,则跟上述一样,修改指针 ij,使其缩小范围;
(2) 当 nums[m] == target 时,说明找到目标元素,可采用 j = m − 1 j=m-1 j=m1 来缩小搜索区间,这可保持小于 target 的元素在 [ 0 , m − 1 ] [0, m-1] [0,m1] 中,而指针 j 向着小于 target 元素靠近
完成循环后,i 指向最左边的 target,而 j 指向首个小于 target 的元素。最终,返回索引 i

在这个过程中规定:下标小于 i 的元素一定小于 target,而下标大于 j 的元素是大于等于 target

def binarySearch2(nums, target):
    """二分查找(存在重复元素)"""
    n = len(nums)
    i, j = 0, n-1
    while i <= j:
        m = i + (j-i)//2
        if nums[m] < target:
            i = m+1
        elif nums[m] > target:
            j = m-1
        else:
            j = m-1
    return i

参考:《Hello 算法》

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值