0.概述
0.1.分類
- 靜態查找和動態查找:針對查找表而言的。動態表指查找表中有刪除和插入操作的表。
- 無序查找和有序查找:
無序查找:被查找數列有序無序均可;
有序查找:被查找數列必須爲有序數列。
0.2.平均查找長度
Average Search Length,ASL
需和指定key進行比較的關鍵字的個數的期望值,稱爲查找算法在查找成功時的平均查找長度。
1. 順序查找、線性查找
說明: 順序查找適合於存儲結構爲順序存儲或鏈接存儲的線性表。
基本思想: 屬於無序查找算法。
- 從線形表的一端開始,順序掃描。
- 兩兩比較,若相等則表示查找成功。
- 若掃描結束,仍沒有找到,表示查找失敗。
時間複雜度: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. 二分查找
說明: 元素必須是有序的,如果是無序的則要先進行排序操作。
基本思想: 屬於有序查找算法。
- 從數組的中間元素開始,如果中間元素正好是要查找的元素,則查找過程結束。
- 如果要查找的元素大於/小於中間元素,則在數組大於/小於中間元素的那一半中查找。而且跟開始一樣從中間元素開始比較。
- 如果在某一步驟數組爲空,則代表找不到。
算法步驟:
Input:有序數組A[1…n],查找值T
Output:m
- 令
- 如果,則搜索失敗
- 令中間元素索引
- 如果,令,返回步驟2
- 如果,令,返回步驟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我寫代碼發現,兩個嚴重的問題:
- 在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
- 有博主說在【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
- 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