查找算法、python实现

0.概述

0.1.分类

  • 静态查找和动态查找:针对查找表而言的。动态表指查找表中有删除和插入操作的表。
  • 无序查找和有序查找:
    无序查找:被查找数列有序无序均可;
    有序查找:被查找数列必须为有序数列。

0.2.平均查找长度

Average Search Length,ASL
需和指定key进行比较的关键字的个数的期望值,称为查找算法在查找成功时的平均查找长度。

1. 顺序查找、线性查找

说明: 顺序查找适合于存储结构为顺序存储或链接存储的线性表

基本思想: 属于无序查找算法。

  1. 从线形表的一端开始,顺序扫描。
  2. 两两比较,若相等则表示查找成功。
  3. 若扫描结束,仍没有找到,表示查找失败。

时间复杂度:O(n)

def sequential_search(lis, key):
    length = len(lis)
    for i in range(length):
        if lis[i] == key:
            return i
    else: # 注意在for外面
        return False

if __name__ == '__main__':
    LIST = [1, 5, 8, 123, 22, 54, 7, 99, 300, 222]
    result = sequential_search(LIST, 123)

2. 二分查找

说明: 元素必须是有序的,如果是无序的则要先进行排序操作。

基本思想: 属于有序查找算法。

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

算法步骤
Input:有序数组A[1…n],查找值T
Output:m

  1. L = 0 , R = n − 1 L=0,R=n-1 L=0,R=n1
  2. 如果 L > R L>R L>R,则搜索失败
  3. 令中间元素索引 m = ⌊ L + R 2 ⌋ m=⌊\frac{L+R}2⌋ m=2L+R
  4. 如果 A [ m ] < T A[m]<T A[m]<T,令 L = m + 1 L=m+1 L=m+1,返回步骤2
  5. 如果 A [ m ] > T A[m]>T A[m]>T,令 R = m − 1 R=m-1 R=m1,返回步骤2

复杂度分析:
时间复杂度:O(logn),每次把搜索区域减少一半
空间复杂度:O(1)

def binary_search(nums, key):
    l, r = 0, len(nums)-1
    
    while l <= r:
        m = (l + r) // 2
        
        if nums[m] == key:
            return m
        elif nums[m] > key: # 在左半边
            r = m - 1
        else:
            l = m + 1 # 在右半边
            
    return False
 
if __name__ == '__main__':
    nums = [1, 5, 7, 8, 22, 54, 99, 123, 200, 222, 444]
    result = binary_search(nums, 99) # return 6
def binary_search(nums, key, l, r):
    if l > r: return False
    
    m = (l + r) // 2
    if nums[m] == key:
        return m
    elif nums[m] > key: # 在左半边
        return binary_search(nums, key, l, m-1)
    else: # 在右半边
        return binary_search(nums, key, m+1, r)

if __name__ == '__main__':
    nums = [1, 5, 7, 8, 22, 54, 99, 123, 200, 222, 444]
    result = binary_search(nums, 99, 0, len(nums)-1) # return 6
    print(result)

3. 插值查找

与二分查找的区别:
在这里插入图片描述
因此,代码和二分查找仅有m赋值的区别。

def insert_search(nums, key, l, r):
    if l > r: return False
    
    m = l + int(((key - nums[l])/(nums[r] - nums[l]))*(r - l)) #二分查找为(l + r) // 2

    if nums[m] == key:
        return m
    elif nums[m] > key: # 在左半边
        return binary_search(nums, key, l, m-1)
    else: # 在右半边
        return binary_search(nums, key, m+1, r)

if __name__ == '__main__':
    nums = [1, 5, 7, 8, 22, 54, 99, 123, 200, 222, 444]
    result = insert_search(nums, 99, 0, len(nums)-1) # return 6
    print(result)

emmm我写代码发现,两个严重的问题:

  1. 在nums很短、key很大时,很可能会报错!如下所示:
if __name__ == '__main__':
    nums = [1, 5, 7]
    result = insert_search(nums, 99, 0, len(nums)-1)
    # m = 32, nums[32]
    # IndexError: list index out of range
  1. 博主说在【m=…】后,加入如下判断,可以避免1中问题。
m = l + int(((key - nums[l])/(nums[r] - nums[l]))*(r - l))
if(mid < low or mid > high): return False

然而,只是错觉,如下所示:

if __name__ == '__main__':
    nums = [1, 5, 7,999,7]
    result = insert_search(nums, 999, 0, len(nums)-1) # return False
    # m = 665, l = 0, r = 4
    # 进入if,return False
  1. m的计算容易出现“除数是0”的情况。
if __name__ == '__main__':
    nums = [1, 5, 7,999,7,1]
    result = insert_search(nums, 999, 0, len(nums)-1)
    # ZeroDivisionError

综上:
 对于表长较大,而关键字分布又比较均匀的查找表来说,插值查找算法的平均性能比二分查找要好的多。
 反之,数组中如果分布非常不均匀,那么插值查找未必是很合适的选择。
 尤其是,nums短,要查找的数很大时,不能使用!

复杂度分析:
时间复杂性:如果元素均匀分布,则O(log(logn)),在最坏的情况下可能需要 O(n)。
空间复杂度:O(1)。

4. 斐波那契查找

留坑!一天一个吧!

5. 树表查找

6. 分块查找

7. 哈希查找

参考

https://www.cnblogs.com/lsqin/p/9342929.html
https://www.cnblogs.com/yw09041432/p/5908444.html
https://blog.csdn.net/xiaodongdonglht/article/details/95044327

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值