查找算法:二分查找、插值查找、斐波那契查找

二分查找

查找的前提是数组有序

思路分析

代码实现(递归实现)

# 二分查找(递归法实现)
# 找到一个相等的值就返回该值的下标
def binary_search(arr: list, find_val: int, left: int, right: int):
    mid = (left + right) // 2  # 寻找数组中间位置的下标

    if left > right:  # 找不到元素,返回-1
        return -1

    if find_val == arr[mid]:  # 找到了元素,返回元素在数组中的下标
        return mid
    elif find_val < arr[mid]:  # 要找的元素比数组中间元素小,则继续在 mid 的左边寻找
        return binary_search(arr, find_val, left, mid - 1)
    else:  # 要找的元素比数组中间元素大,则继续在 mid 的右边寻找
        return binary_search(arr, find_val, mid + 1, right)


arr = [4, 8, 9, 10, 12, 14, 15]
res = binary_search(arr, 0, 0, len(arr) - 1)
print(res)


# 二分查找(递归法实现)
# 如果数组中有多个值和要查找的值相等,则把它们的下标都返回
def binary_search2(arr: list, find_val: int, left: int, right: int):
    mid = (left + right) // 2  # 寻找数组中间位置的下标

    if left > right:  # 找不到元素,返回-1
        return []

    if find_val == arr[mid]:  # 找到了元素,返回元素在数组中的下标
        # 找到了元素,先把当前的下标放入到一个列表中
        # 再依次从该位置开始向左和向右查找,还有没有其他相等的值
        index_pos = []
        index_pos.append(mid)

        temp = mid - 1
        while True:  # 向左查找
            if temp < left or arr[temp] != find_val:
                break
            index_pos.append(temp)
            temp -= 1  # temp 左移

        temp = mid + 1
        while True:  # 向右查找
            if temp > right or arr[temp] != find_val:
                break
            index_pos.append(temp)
            temp += 1  # temp 右移

        return index_pos
    elif find_val < arr[mid]:  # 要找的元素比数组中间元素小,则继续在 mid 的左边寻找
        return binary_search2(arr, find_val, left, mid - 1)
    else:  # 要找的元素比数组中间元素大,则继续在 mid 的右边寻找
        return binary_search2(arr, find_val, mid + 1, right)


arr = [4, 8, 9, 10,10, 12, 14, 15]
res = binary_search2(arr, 0, 0, len(arr) - 1)
print(res)

代码实现(非递归实现)

# 二分查找的非递归实现
def binary_search(arr: list, target: int):
    """
    二分查找的非递归实现
    :param arr: 要查找的有序数组
    :param target: 要查找的值
    :return: 要查找值的下标,找不到返回-1
    """
    left = 0
    right = len(arr) - 1
    while left <= right:
        mid = (left + right) // 2
        if target == arr[mid]:
            return mid
        elif target < arr[mid]:
            right = mid - 1
        else:
            left = mid + 1

    return -1


res = binary_search([1, 2, 5, 7, 9, 10, 12], 12)
print(res)

插值查找

查找的前提是数组有序

思路分析

当数组为[1, 2, 3, 4, 5, ..., 100] 时,加入此时要查找1,那么二分查找的方法会进行多次递归才能找到,效率较低,所以有了插值查找方法。插值查找适用于数据比较连续的情况下。

代码实现

# 插值查找
def insert_value_search(arr: list, find_val: int, left: int, right: int):
    # 找不到元素,返回-1
    # 条件 find_val < arr[left] or find_val > arr[right] 要有,否则mid可能会越界
    if left > right or find_val < arr[left] or find_val > arr[right]:  
        return -1
    # 寻找数组中间位置的下标
    mid = left + (right - left) * (find_val - arr[left]) // (arr[right] - arr[left])
    mid_val = arr[mid]
    if find_val == mid_val:  # 找到了元素,返回元素在数组中的下标
        return mid
    elif find_val < mid_val:  # 要找的元素比数组中间元素小,则继续在 mid 的左边寻找
        return binary_search(arr, find_val, left, mid - 1)
    else:  # 要找的元素比数组中间元素大,则继续在 mid 的右边寻找
        return binary_search(arr, find_val, mid + 1, right)


arr = []
for i in range(100):
    arr.append(i + 1)
res = insert_value_search(arr, 3, 0, len(arr) - 1)
print(res)

斐波那契查找

查找的前提是数组有序

思路分析

代码实现

import copy
# 斐波那契查找
# 因为算法中需要用到斐波那契数列,所以此处用非递归的方法构造一个斐波那契数列
def fib(size: int) -> list:
    f = []
    f.append(1)
    f.append(2)
    i = 2
    while i < size:
        f.insert(i, f[i - 1] + f[i - 2])
        i += 1

    return f


# 非递归方式实现斐波那契查找算法
def fibonacci_search(arr, key):
    low = 0
    high = len(arr) - 1
    k = 0  # 表示斐波那契分割数值的下标
    mid = 0  # 存放 mid 值
    f = fib(20)  # 获取斐波那契数列
    # 获取斐波那契分割数值的下标
    while high > f[k] - 1:
        k += 1

    # 因为f[k]的值可能大于arr的长度,因此需要对数组进行扩容(新构造一个数组)
    # 让数组的长度等于f[k],新增加的长度的下标用arr数组最后的数填充
    # 如:arr=[1,8,10,89,1000,1234]  f[k] = 8
    # 扩容后:temp=[1,8,10,89,1000,1234,1234,1234]
    temp = copy.deepcopy(arr)
    i = high + 1
    while i < f[k]:
        temp.append(arr[high])
        i += 1

    # 使用 while 来循环处理,找到要查找的key
    while low <= high:  # 只要条件满足就可以继续查找
        mid = low + f[k - 1] - 1
        if key < temp[mid]:  # 应该继续向数组的前面查找(左边)
            high = mid - 1
            """
            k -= 1 说明:
            数组全部元素 = 前面(左边)的元素 + 后面(右边)的元素
            斐波那契数列:f[k] = f[k - 1] + f[k - 2]
            因为前面有f[k - 1]个元素,所以可以继续拆分:f[k - 1] = f[k - 2] + f[k - 3]
            即在f[k - 1] 的前面继续查找,即下次循环 mid = f[k - 1 - 1] - 1 
            """
            k -= 1
        elif key > temp[mid]:  # 应该继续向数组的后面查找(右边)
            low = mid + 1
            """
            k -= 2  说明:
            数组全部元素 = 前面(左边)的元素 + 后面(右边)的元素
            斐波那契数列:f[k] = f[k - 1] + f[k - 2]
            因为后面有f[k - 2]个元素,所以可以继续拆分:f[k - 2] = f[k - 3] + f[k - 4]
            即在f[k - 2] 的前面继续查找 k-=2,即下次循环 mid = f[k - 1 - 2] - 1 
            """
            k -= 2
        else:  # 找到
            # 确定返回的是哪个下标
            if mid <= high:
                return mid
            return high

    # 找不到,返回-1
    return -1


arr=[1,8,10,89,1000,1234]
res = fibonacci_search(arr, 8)
print(res)

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值