一、查找/搜索
- 定义:搜索是在元素集合中查找特定元素的算法过程。搜索通常返回元素是否存在(布尔值),有时也返回元素的位置。
- Python 中的简单搜索:使用
in
运算符可以快速判断元素是否在列表中。
>>> 15 in [3, 5, 2, 4, 1]
False
>>> 3 in [3, 5, 2, 4, 1]
True
二、顺序查找
原理
- 从列表的第一个元素开始,依次比较每个元素,直到找到目标元素或遍历完整个列表。
- 如果列表是无序的,顺序查找是唯一的选择。
代码实现
def sequentialSearch(alist, item):
pos = 0
found = False
while pos < len(alist) and not found:
if alist[pos] == item:
found = True
else:
pos += 1
return found
testlist = [1, 2, 32, 8, 17, 19, 42, 13, 0]
print(sequentialSearch(testlist, 3)) # False
print(sequentialSearch(testlist, 13)) # True
分析
- 时间复杂度:O(n)
- 最好情况:O(1)(目标元素在列表开头)。
- 最坏情况:O(n)(目标元素在列表末尾或不存在)。
- 平均情况:O(n/2) ≈ O(n)。
三、有序列表的顺序查找
原理
- 如果列表是有序的,可以在查找过程中提前终止。如果当前元素大于目标元素,则目标元素不存在。
代码实现
def orderedSequentialSearch(alist, item):
pos = 0
found = False
stop = False
while pos < len(alist) and not found and not stop:
if alist[pos] == item:
found = True
else:
if alist[pos] > item:
stop = True
else:
pos += 1
return found
testlist = [0, 1, 2, 8, 13, 17, 19, 32, 42]
print(orderedSequentialSearch(testlist, 3)) # False
print(orderedSequentialSearch(testlist, 13)) # True
分析
- 时间复杂度:O(n)
- 最好情况:O(1)(目标元素在列表开头或目标元素小于列表中所有元素)。
- 最坏情况:O(n)(目标元素在列表末尾)。
- 平均情况:O(n/2) ≈ O(n)。
四、二分查找
原理
- 利用列表的有序性,每次比较中间元素,根据目标值与中间值的大小关系,选择左半部分或右半部分继续查找。
- 每次比较后,搜索范围减半。
代码实现
def binarySearch(alist, item):
first = 0
last = len(alist) - 1
found = False
while first <= last and not found:
midpoint = (first + last) // 2
if alist[midpoint] == item:
found = True
else:
if item < alist[midpoint]:
last = midpoint - 1
else:
first = midpoint + 1
return found
testlist = [0, 1, 2, 8, 13, 17, 19, 32, 42]
print(binarySearch(testlist, 3)) # False
print(binarySearch(testlist, 13)) # True
分析
- 时间复杂度:O(log n)
- 每次比较后,搜索范围减半,因此最多需要 log₂(n) 次比较。
五、哈希查找
原理
- 利用哈希函数将元素映射到哈希表的某个位置,从而实现快速查找。
- 哈希函数需要满足:
- 快速计算。
- 尽量减少冲突(多个元素映射到同一个位置)。
哈希函数
- 简单余数法:
h(item) = item % table_size
- 分组求和法:将元素的数字分组求和后取余。
- 平方取中法:先平方,再取中间几位数字,最后取余。
冲突解决
- 开放寻址法:从冲突位置开始,顺序查找下一个空槽。
- 线性探测:如果槽
i
被占用,则尝试槽 i+1
,直到找到空槽。
代码实现
class HashTable:
def __init__(self):
self.size = 11 # 哈希表大小,通常选择质数
self.slots = [None] * self.size # 存储键
self.data = [None] * self.size # 存储值
def put(self, key, data):
hashvalue = self.hashfunction(key, len(self.slots))
if self.slots[hashvalue] is None:
self.slots[hashvalue] = key
self.data[hashvalue] = data
else:
if self.slots[hashvalue] == key:
self.data[hashvalue] = data # 替换旧值
else:
nextslot = self.rehash(hashvalue, len(self.slots))
while self.slots[nextslot] is not None and self.slots[nextslot] != key:
nextslot = self.rehash(nextslot, len(self.slots))
if self.slots[nextslot] is None:
self.slots[nextslot] = key
self.data[nextslot] = data
else:
self.data[nextslot] = data # 替换旧值
def get(self, key):
startslot = self.hashfunction(key, len(self.slots))
data = None
stop = False
found = False
position = startslot
while self.slots[position] is not None and not found and not stop:
if self.slots[position] == key:
found = True
data = self.data[position]
else:
position = self.rehash(position, len(self.slots))
if position == startslot:
stop = True
return data
def hashfunction(self, key, size):
return key % size
def rehash(self, oldhash, size):
return (oldhash + 1) % size
def __getitem__(self, key):
return self.get(key)
def __setitem__(self, key, data):
self.put(key, data)
使用示例
H = HashTable()
H[54] = "cat"
H[26] = "dog"
H[93] = "lion"
H[17] = "tiger"
H[77] = "bird"
H[31] = "cow"
H[44] = "goat"
H[55] = "pig"
H[20] = "chicken"
print(H.slots)
print(H.data)
print(H[20]) # 输出: chicken
print(H[17]) # 输出: tiger
H[20] = "duck"
print(H[20]) # 输出: duck
print(H[99]) # 输出: None
分析
- 时间复杂度:O(1)(理想情况下)
- 如果哈希函数设计良好且冲突较少,查找操作可以在常数时间内完成。
- 冲突问题:冲突可能导致查找时间增加,但通过合理设计哈希函数和冲突解决机制,可以尽量减少冲突。