二分查找法

二分查找法

1. 一般二分查找法

二分查找适用查找序列:对于有序序列的查找
二分查找法的时间复杂度为O(logN)
二分查找的主要思路是:对于有序序列(假设为非递减序列),每次找到左右区间的中间位置,比较查找目标元素与中间值的大小,如果目标元素值小于中间值,则向左缩减查找的区间,否则向右缩减区间值。
对于给定闭区间[a, b], left= a, right = b, mid = left + int((right - left) / 2) ,此处使用减法操作是为了防止溢出。
(1)input_arr[mid] > target_num: right = mid -1
(2)input_arr[mid] < target_num: left = mid + 1
(3)input_arr[mid] = target_num: return mid;
没有找到目标元素值,则返回-1.

python实现:

# 一般二分查找
def binary_search(input_list, target):

    if len(input_list) == 0:
        return -1

    left = 0
    right = len(input_list) - 1

    while left <= right:
        # 一般思路为:mid = (left + right) / 2, 但是该计算方法可能造成加法溢出
        mid = int(left + (right - left) / 2)
        if input_list[mid] == target:
            return mid
        elif input_list[mid] > target:
            right = mid - 1
        else:
            left = mid + 1
    return -1
递归实现二分查找法

递归在性能上会略差,但是其性能差异是常数级别

# 用递归的方法实现二分查找法
def r_binary_search(input_arr, left, right, target):

    mid = left + int((right - left) / 2)
    if input_arr[mid] == target:
        return mid

    if input_arr[mid] < target:
        left = mid + 1
    else:
        right = mid - 1

    r_binary_search(input_arr, left, right, target)
2. 带有重复元素的二分查找法,获取最左侧的元素index
# 带有重复元素的二分查找,获取最左侧的index值
def repeat_binary_search(input_list, target):
    if len(input_list) == 0:
        return -1

    left = 0
    right = len(input_list) - 1

    # 如果大于或等于目标值则right左移动到mid位置,小于目标值则left右移动到mid+1位置
    # 在input_list[mid] >= target的情况下,目标解的位置为[left, mid]闭区间
    # 循环终止的条件为:left=right

    while left < right:
        mid = int(left + (right - left) / 2)
        
        if input_list[mid] >= target:
            right = mid
        else:
            left = mid + 1

    return left
实现floor()/ceil()函数

floor():对于重复元素的查找
如果找到target, 返回第一个target相应的索引index
如果没有找到target, 返回比target小的最大值相应的索引, 如果这个最大值有多个, 返回最大索引
如果这个target比整个数组的最小元素值还要小, 则不存在这个target的floor值, 返回-1

# 对于重复元素的查找
# 如果找到target, 返回第一个target相应的索引index
# 如果没有找到target, 返回比target小的最大值相应的索引, 如果这个最大值有多个, 返回最大索引
# 如果这个target比整个数组的最小元素值还要小, 则不存在这个target的floor值, 返回-1
def floor(input_arr, target):

    if target < input_arr[0]:
        return -1

    # 查找目标元素的区间为[left, right],此处为左闭右闭
    left = 0
    right = len(input_arr) - 1

    while left < right:
        # 使用向上取整避免死循环
        mid = left + int((right - left + 1) / 2)
        # 需要返回第一个target的索引值,所以需要右边值区间往左移动
        if input_arr[mid] < target:
            left = mid
        else:
            right = mid - 1

    if left < len(input_arr) - 1 and input_arr[left + 1] == target:
        return left + 1

    return left

ceil()函数:二分查找法, 在有序数组arr中, 查找target
如果找到target, 返回最后一个target相应的索引index
如果没有找到target, 返回比target大的最小值相应的索引, 如果这个最小值有多个, 返回最小的索引
如果这个target比整个数组的最大元素值还要大, 则不存在这个target的ceil值, 返回整个数组元素个数n

# 二分查找法, 在有序数组arr中, 查找target
# 如果找到target, 返回最后一个target相应的索引index
# 如果没有找到target, 返回比target大的最小值相应的索引, 如果这个最小值有多个, 返回最小的索引
# 如果这个target比整个数组的最大元素值还要大, 则不存在这个target的ceil值, 返回整个数组元素个数n
def ceil(input_arr, target):
    if target > input_arr[-1]:
        return len(input_arr)

    left = 0
    right = len(input_arr) - 1

    while left < right:
        # 使用向下取整避免死循环
        mid = left + int((right - left - 1) / 2)
        # 需要返回最后一个target的索引值,所以需要左边值区间往右移动
        if input_arr[mid] <= target:
            left = mid + 1
        else:
            right = mid

    if right >= 0 and input_arr[right - 1] == target:
        return right - 1

    return right
3. 求元素开平方

大于1的元素开平方,要限制元素的范围是大于等于1,对小于1的元素左右区间设置不一致

# 求开方: x开方值应该在(0, x]之间
def sqrt_binary_search(target_num):
    try:
        assert target_num >= 1
        left = 0
        right = target_num

        # 目标区间为左开右闭 (left, right]
        while left < right:
            mid = left + (right - left) / 2
            divide_ = target_num / mid
            if abs(divide_ - mid) < 0.00001:
                return mid
            if divide_ <= mid:
                right = mid
            else:
                left = mid

        return -1
    except AssertionError:
        print("输入开根号的数据不符合规范")
        return None
4. 求大于给定元素的最小元素
# 求大于给定元素的最小元素, 找到返回元素值,否则返回第一个元素值
def next_smallest(input_list, target):
    input_length = len(input_list)

    assert input_length >= 1

    left = 0
    right = len(input_list) - 1

    # 对于有重复元素的列表仍然适用
    while left <= right:
        mid = left + int((right - left) / 2)

        if input_list[mid] <= target:
            left = mid + 1
        else:
            right = mid - 1

    return input_list[left] if left < input_length else input_list[0]
5. 查找区间
#  查找区间:返回一个元素(可以重复)在数组中的位置区间
def find_element_span(input_list, target):
    first_index = repeat_binary_search(input_list, target)

    if input_list[-1] == target:
        last_index = len(input_list) - 1
    else:
        last_index = repeat_binary_search(input_list, target + 1) - 1

    if first_index == len(input_list) or input_list[first_index] != target:
        return [-1, -1]
    else:
        return [first_index, last_index]

每天进步一点点…

上述代码均通过小样本测试用例,如若有误还望指正。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值