python实现各种常用算法之搜索算法(13)

python实现搜索算法(二)


跳跃搜索

跳跃搜索(Jump search),按照固定步长,从有序表的首项步进,直到匹配到符合目标元素的区间,然后在该区间使用线性搜索,找到目标元素的确切位置。

跳跃搜索的思路如下:给定大小 n 的有序数组 a,目标元素为 x 和跳跃的步长 m ,然后搜索 a[0],a[1],a[2],a[3]…a[km]…一旦我们找到区间 a[km]< target < a[(k+1)m],然后在此区间进行线性搜索找到目标元素 x 的确切位置。

在最坏的情况下,我们必须进行 n/m 跳转,如果最后一个检查值大于要搜索的元素,则我们对线性搜索进行 m-1 比较。因此,最坏情况下的比较总数将为((n/m)+m-1)。当 m =n^(1/2)时,函数((n/m)+m-1)的值将为最小值。因此,最好的步长是 m=n^(1/2)

算法原理
适用性: 跳跃搜索的前提是有序表存储。相比于二分搜索,跳跃搜索仅遍历一次,而二分搜索最多需要 O(logn),如果向后跳跃比向前跳跃花费更多时间,考虑要搜索的元素是最小元素或小于最小的,我们一般选用跳跃搜索。

基本思想基于二分搜索算法,采用固定间隔进行跳跃,直到找到一个符合目标元素的区间,然后在该区间使用线性搜索,找到目标元素的确切位置。

复杂度分析

  • 最坏复杂度:时间复杂度为 O(n^(1/2))

  • 最好复杂度:时间复杂度为 O(n^(1/2))

  • 平均复杂度:时间复杂度为 O(n^(1/2))

算法实现
import math
'''
思路:采用固定间隔进行跳跃,直到找到一个符合目标元素的区间,然后在该区间使用线性搜索,找到目标元素的确切位置
'''
def jumpsearch(sorted_sequence, target):
    #序列长度
    n = len(sorted_sequence)
    #步长为长度开方
    step = int(math.floor(math.sqrt(n)))
    #记录下标值
    prev = 0
    #取(步长与序列长度的最小值)-1为下标的序列值,小于目标值则继续循环
    while sorted_sequence[min(step, n)-1] < target:  # 按照固定步长跳跃
        #下标值记录步长
        prev = step
        #步长累加
        step = step+int(math.floor(math.sqrt(n)))
        #步长》=序列长度,则遍历一遍没有找到符合条件的目标值,返回空
        if prev >= n:
            return None
    #当前下标的序列值小于目标值,继续循环
    while sorted_sequence[prev] < target:  # 线性搜索
        #线性搜索,依次向前比较
        prev = prev+1
        #下标值满足条件代表遍历序列一遍返回空
        if prev == min(step, n):
            return None
    #如果下标的序列值等于目标值,返回下标,否则返回空
    if sorted_sequence[prev] == target:
        return prev
    else:
        return None


if __name__ == '__main__':
    sorted_sequence = [i for i in range(1, 10001, 2)]
    target = 521
    index = jumpsearch(sorted_sequence, target)
    print(index)

快速搜索

快速搜索(quick search),快速选择是一种选择算法,用于查找无序列表中的第 k 个最小元素,它与快速排序算法有关。

快速搜索使用与快速排序相同的整体方法,选择一个元素作为数据透视表,并根据数据透视表将数据分成两部分,因此小于或大于数据透视表。但是,不要像在快速排序中那样递归到双方,而是快速选择仅向一侧递归——与正在搜索的元素的一侧。这将平均复杂度从 O(nlogn) 降低到 o(n),最坏情况是 O(n^2)。

算法原理
适用性: 用于查找无序列表中的第 k 个最小元素或最大元素。

基本思想: 基于二分搜索算法,采用固定间隔进行跳跃,直到找到一个符合目标元素的区间,然后在该区间使用线性搜索,找到目标元素的确切位置。

复杂度分析

  • 最坏复杂度:时间复杂度为 O(n^2)。

  • 最好复杂度:时间复杂度为 O(n)。

  • 平均复杂度:时间复杂度为 O(n)。

算法实现
import random

'''思路:和快速排序相同,设置一个对应的分区函数,其作用是对给定元素,对列表执行分为两部分的操作:
小于指定元素的部分,大于或等于指定元素的部分。'''
def partition(sequence, left, right, pivot_index):
    pivot_value = sequence[pivot_index]
    # 交换两个元素,使pivot_index与最右边元素置换位置,即先将pivot移动到最右边
    sequence[pivot_index], sequence[right] = sequence[right], sequence[pivot_index]
    store_index = left
    for i in range(left, right):
        if sequence[i] < pivot_value:
            # 交换两个元素,使当前遍历元素(小于pivot_value的元素)与store_index元素置换位置
            sequence[store_index], sequence[i] = sequence[i], sequence[store_index]
            store_index = store_index+1  # store_index索引增加1
    # 交换两个元素,使store_index与最右边元素置换位置,即交换回来pivot最终应该在的位置
    sequence[store_index], sequence[right] = sequence[right], sequence[store_index]
    return store_index


def quick_search(sequence, left, right, k):
    if left == right:  # 如果只有一个元素
        return sequence[left]  # 返回该元素
    # 初始 pivot_index,使 pivot_index 在无序表随机
    pivot_index = left+random.randint(0, right-left+1)
    # pivot 在已经排好序的位置
    pivot_index = partition(sequence, left, right, pivot_index)
    if k == pivot_index:
        return sequence[k]  # 返回该位置元素
    elif k < pivot_index:
        # 需要在[left,pivot_index-1]里面继续快速检索
        return quick_search(sequence, left, pivot_index-1, k)
    else:
        # 需要在[pivot_index+1,right]里面继续快速检索
        return quick_search(sequence, pivot_index+1, right, k)


if __name__ == '__main__':
    sequence = [12, 1, 21, 34, 25, 15, 35, 13, 45, 100, 234, 521, 345, 16, 1314]
    left = 0
    right = len(sequence)-1
    k = int(input("Find the k'th smallest number in sequence,k="))-1
    value = quick_search(sequence, left, right, k)
    print("The %s 'th smallest number in sequence is : %s" % (k+1, value))

哈希搜索

哈希表就是一种以键-值(key-indexed) 存储数据的结构,只要输入待查找的键即 key,即可查找到其对应的值。哈希表是一个在时间和空间上做出权衡的经典例子。如果没有内存限制,那么可以直接将键作为数组的索引。那么所有的查找时间复杂度为 O(1);如果没有时间限制,那么我们可以使用无序数组并进行顺序查找,这样只需要很少的内存。哈希表使用了适度的时间和空间来在这两个极端之间找到了平衡。只需要调整哈希函数算法即可在时间和空间上做出取舍。

算法原理
适用性: 一个简单无序数组即可实现索引关系。

基本思想: 哈希的思路很简单,如果所有的键都是整数,那么就可以使用一个简单的无序数组来实现:将键作为索引,值即为其对应的值,这样就可以快速访问任意键的值。这是对于简单的键的情况,我们将其扩展到可以处理更加复杂的类型的键。

复杂度分析

  • 最坏复杂度:时间复杂度为 O(1),对于无冲突的哈希表而言。

  • 最好复杂度:时间复杂度为 O(1),对于无冲突的哈希表而言。

  • 平均复杂度:时间复杂度为 O(1),对于无冲突的哈希表而言。

算法实现
'''思路:建立哈希表,完成索引结构,即实现哈希搜索的功能'''
class HashTable:
    def __init__(self, size):
        # 使用list数据结构作为哈希表元素保存方法
        self.elem = [None for i in range(size)]
        self.count = size  # 最大表长

    def hash(self, key):
        # 散列函数采用除留余数法
        return key % self.count

    def insert_hash(self, key):
        # 插入关键字到哈希表内
        address = self.hash(key)  # 求散列地址
        # 当前位置已经有数据了,发生冲突。
        while self.elem[address]:
            # 线性探测下一地址是否可用
            address = (address+1) % self.count
        # 没有冲突则直接保存。
        self.elem[address] = key

    def search_hash(self, key):
        # 查找关键字,返回布尔值
        star = address = self.hash(key)
        while self.elem[address] != key:
            address = (address + 1) % self.count
            # 说明没找到或者循环到了开始的位置
            if not self.elem[address] or address == star:
                return False
        return True, address  # 返回索引值


if __name__ == '__main__':
    list_a = [12, 67, 56, 16, 25, 37, 22, 29, 15, 47, 48, 34]
    hash_table = HashTable(12)
    for i in list_a:
        hash_table.insert_hash(i)
    for i in hash_table.elem:
        if i:
            print((i, hash_table.elem.index(i)), end=" ")
    print("\n")
    print(hash_table.search_hash(15))
    print(hash_table.search_hash(33))

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

jia666666

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值