最近打算把常用的排序和搜索算法都用python实现一下,但是发现网上的大多数程序实际运行的时候都有问题,要么是陷入循环出不来要么是列表中有的元素查找失败。所以自己实现了一下,经过实际运行,都可以正常工作。贴出来和大家分享,如有问题,欢迎指正!

一、 线性查找

    线性查找是最基础的查找方法,主要用于无序表。将待查找列表中的元素逐个与目标元素进行比较。

    算法分析:如果目标元素刚好位于列表中第一个,则此时算法复杂度为O(1),若目标元素位于列表中的最后一个,那么找到最后一个元素时才能找到,则此时算法复杂度为O(n),平均查找次数为(n + 1) / 2,最终时间复杂度为O(n)。

代码如下:



from numpy import random
import numpy as np

def linear_search(list, key):
    len_of_list = len(list)
    result = np.nan
    for i in range(0, len_of_list):
        if list[i] == key:  #遍历列表进行搜索
            result = i
    return result

二、 二分查找

    二分查找适用于有序表,每次将待查询列表中间位置的元素与目标元素相比较。通过迭代,不断缩小搜索范围。

代码如下:

def binary_search(list, key):
    list = np.sort(list)
    print(list)
    len_of_list = len(list)
    start = 0
    stop = len_of_list - 1
    result = np.nan
    while stop - start >= 2:
        mid = int((start + stop) / 2)
        if list[mid] < key:
            start = mid + 1
        elif list[mid] > key:
            stop = mid - 1
        else:
            result = mid #此时list[mid] = key, 则返回mid,并跳出循环
            break
    # 若在stop - start >= 2的条件下未能查找到key,则不能再使用折半查找
    # 因为mid = int((start + stop) / 2)会导致只能查到较小的那个数
    if list[start] == key:
        result = start
    elif list[stop] == key:
        result = stop
    return result

三、 插值查找

    插值查找的前提假设是数据在待查找列表中的分布是均匀分布的,通过公式

mid = start + int((key - list[start])/(list[stop] - list[start])*(stop - start))

来估计目标元素在列表中的位置,但是未必符合实际情况。

代码如下:

def insert_search(list, key):
    list = np.sort(list)
    print(list)
    len_of_list = len(list)
    start = 0
    stop = len_of_list - 1
    result = np.nan
    while stop - start >= 2:
        #插入搜索相当于假设数据在最小值和最大值的闭区间内是线性增加的,或者说表内数据的
        #取值是服从均匀分布的,但未必符合实际情况
        mid = start + int((key - list[start])/(list[stop] - list[start])*(stop - start))
        if list[mid] < key:
            start = mid + 1
        elif list[mid] > key:
            stop = mid - 1
        else:
            result = mid  #此时list[mid] = key, 则返回mid,并跳出循环
            break
    return result

四、 fibonacci查找

    fibonacci查找与二分查找类似,但利用了fibonacci数列的特点,即

fibo_list[i] = fibo_list[i-1] + fibo_list[i-2]

利用这一公式来对待查找列表进行分割,前提要求是待查找列表中的元素个数比某个fibonacci数小1,即

fibo_list[fibo_idx] - 1 = len_of_list

详细解释见代码注释

代码如下:

def fibonacci_search(list, key):
    list = np.sort(list)
    print(list)
    list = list.tolist()  #将ndarray格式的数据转换为list格式
    len_of_list = len(list)
    len_of_list_ori = len_of_list  #记录原始待查询数列的长度
    #定义fibonacci数列
    fibo_list = []
    f0 = 0
    f1 = 1
    fibo_list.append(f0)
    fibo_list.append(f1)
    len_of_fibo = 20   #fibonacci数列的长度
    i = 2
    #生成fibonacci数列
    while i < len_of_fibo:
        temp = fibo_list[i-1] + fibo_list[i-2]
        fibo_list.append(temp)
        i += 1
    print(fibo_list) #打印构造好的fibonacci数列
    #找到第一个比待查询数列长度大的fibo_list中的元素
    fibo_idx = 0
    while fibo_list[fibo_idx] - 1 < len_of_list:
        fibo_idx += 1
    print(fibo_idx)
    print(fibo_list[fibo_idx])
    # 对待查询数列进行填充
    while len_of_list < fibo_list[fibo_idx] - 1:
        list.append(list[len_of_list - 1])
        len_of_list += 1
    #查询主程序
    start = 0
    stop = len_of_list - 1
    result = np.nan
    while start <= stop:
        mid = start + fibo_list[fibo_idx - 1] - 1
        if key < list[mid]:
            stop = mid - 1
            fibo_idx -= 1  #说明此时元素应该在左半区间
        elif key > list[mid]:
            start = mid + 1  #说明此时元素应该在右半区间
            fibo_idx -= 2
        else:
            if mid > len_of_list_ori - 1:
                result = len_of_list_ori - 1
                break  #此时查询到的是list中复制的数据
            else:
                result = mid
                break  #此时查询到的是原list中的数据
    print("\n")
    return result

测试代码:

#测试
random.seed(123456)  #设置随机数种子,保证每次运行结果一致
list = random.randint(-100, 100, size=16)
key = 2
print(list)
print(linear_search(list, key))
print(binary_search(list, key))
print(insert_search(list, key))
print(fibonacci_search(list, key))

运行结果如下:

[-35 -51 -44  71 -57  -9 -68  64  36 -90 -88 -25 -80 -53  78   2]
15
[-90 -88 -80 -68 -57 -53 -51 -44 -35 -25  -9   2  36  64  71  78]
11
[-90 -88 -80 -68 -57 -53 -51 -44 -35 -25  -9   2  36  64  71  78]
11
[-90 -88 -80 -68 -57 -53 -51 -44 -35 -25  -9   2  36  64  71  78]
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181]
8
21




11