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))