python實現搜索算法(一)
順序搜索
順序搜索也稱爲線性搜索,屬於無序查找算法。
算法原理
思路:
從數據結構線性表的一端開始,順序掃描,依次將掃描到的結點關鍵字與給定值 k 相比較,
若相等則表示查找成功;
若掃描結束仍沒有找到關鍵字等於 k 的結點,表示查找失敗。
適用性:順序搜索適合於存儲結構爲順序存儲或鏈接存儲的線性表。
複雜度分析
-
最壞複雜度: 從一個線性表依次查找對應項,需要做 n 次查找,在最後一項才查找到對應項或者查找失敗(仍然未查找到對應項),時間複雜度爲 O(n)。
-
最好複雜度: 從一個線性表依次查找對應項,第一項就查找到對應項,時間複雜度爲 O(1)。
-
平均複雜度: 假設每個數據元素的概率相等(1/n),1/n(1+2+3+…+n)=(n+1)/2,所以時間複雜度爲 O(n)。
算法實現
"""
思路:從順序表的頭部依次遍歷元素,判斷是否匹配,
若匹配則查找成功,若不匹配則遍歷下一個元素。
"""
def sequence_search(sequence,target):
for i in range(len(sequence)):
if target==sequence[i]:
return i
return None
if __name__ == '__main__':
sequence=[99,12,33,74,521,13,14]
target=521
print(sequence_search(sequence,target))
二分搜索
二分搜索也稱折半搜索(Binary Search),它是一種效率較高的搜索方法。但是,二分搜索要求線性表必須採用順序存儲結構,而且表中元素按關鍵字有序排列。
基本思想:
- 用給定值 k 先與中間結點的關鍵字比較,中間結點把線性表分成兩個子表,
- 若相等則查找成功;
- 若不相等,再根據 k 與該中間結點關鍵字的比較結果確定下一步查找哪個子表,
- 這樣遞歸進行,直到查找到或查找結束髮現表中沒有這樣的結點。
複雜度分析
總共有 n 個元素。
第 1 次折半:還剩 n/2 個元素
第 2 次折半:還剩 n/4 個元素
第 3 次折半:還剩 n/8 個元素
……
第 k 次折半:還剩 n/2^k 個元素
最壞的情況下,最後還剩 1 個元素,令 n/2^k = 1,得 k=logn,時間複雜度 O(logn)。
-
最壞複雜度:上述分析可以得到時間複雜度爲 O(logn)。
-
最好複雜度:第一次折半就查找到中間元素,時間複雜度爲 O(1)。
-
平均複雜度:時間複雜度爲 O(logn), 證明如下:
算法實現
def binary_search(sorted_sequence,target):
left=0
right=len(sorted_sequence)-1
"""
思路:每次查詢查找範圍內的"中間位置"的結點,
若該節點不是所需,則縮小查找範圍爲前半部分或後半部分。
"""
while(left<=right):
midpoint=(left+right)//2
current_item=sorted_sequence[midpoint]
if current_item==target:
return midpoint
elif target<current_item:
right=midpoint-1
else:
left=midpoint+1
return None
if __name__ == '__main__':
sorted_sequence = [i for i in range(1, 999, 2)]
print(binary_search(sorted_sequence, target=521))
拓展
三分搜索:就是在二分搜索的基礎上,將區間分爲三個區間做判斷,因此存在 5 個條件判斷。
def ternary_search(sorted_sequence, target):
left = 0
right = len(sorted_sequence)-1
while(left <= right):
Third1 = (right-left)//3+left
Third2 = 2*(right-left)//3+left
if(sorted_sequence[Third1] == target):
return Third1
elif(sorted_sequence[Third2] == target):
return Third2
elif(target < sorted_sequence[Third1]):
right = Third1-1
elif(target > sorted_sequence[Third2]):
left = Third2+1
else:
left = Third1+1
right = Third2-1
return None
插值搜索
鍥子
在介紹插值查找之前,首先考慮一個新問題,爲什麼上述算法一定要是折半,而不是折四分之一或者折更多呢?打個比方,在英文字典裏面查“apple”,你下意識翻開字典是翻前面的書頁還是後面的書頁呢?如果再讓你查“zoo”,你又怎麼查?很顯然,這裏你絕對不會是從中間開始查起,而是有一定目的的往前或往後翻。同樣的,比如要在取值範圍 1 ~ 10000 之間 100 個元素從小到大均勻分佈的數組中查找 5,我們自然會考慮從數組下標較小的開始查找。經過以上分析,折半查找這種查找方式,不是自適應的(也就是說是傻瓜式的)。二分查找中查找點計算如下:mid=(left+right)/2, 即 mid=left+1/2*(right-left)
通過類比,我們可以將查找的點改進爲如下: mid=left+(key-a[left])/(a[right]-a[left])*(right-left),也就是將上述的比例參數 1/2 改進爲自適應的,根據關鍵字在整個有序表中所處的位置,讓 mid 值的變化更靠近關鍵字 key,這樣也就間接地減少了比較次數。
算法原理
適用性: 對於表長較大,而關鍵字分佈又比較均勻的查找表來說,插值搜索算法的平均性能比折半搜索要好的多。反之,數組中如果分佈非常不均勻,那麼插值搜索未必是很合適的選擇。
基本思想: 基於二分搜索算法,只是在取"中點"的時候把比例參數 1/2 修改爲自適應參數,可以提高搜索效率。當然,差值搜索也屬於有序搜索。
複雜度分析
-
最壞複雜度:時間複雜度爲 O(logn),最壞的情況就是二分搜索的情況,時間複雜度和二分搜索的時間複雜度相同。
-
最好複雜度:時間複雜度爲 O(1)。
-
平均複雜度:時間複雜度爲 O(loglogn)
算法實現
def insert_search(sorted_sequence, target):
left = 0
#右側取序列最大值-1
right = len(sorted_sequence)-1
while(left <= right):
#自適應中點值=左+((目標值-最小值)*數組長度)//(最大值-最小值)
midpoint = left + ((target-sorted_sequence[left])*(right-left))//(
sorted_sequence[right]-sorted_sequence[left]) # 比例參數修改
#中點值小於0或者大於等於最大長度界限,不存在,返回空
if midpoint < 0 or midpoint >= len(sorted_sequence):
return None
current_item = sorted_sequence[midpoint]
#當前值等於目標值,返回中點值
if current_item == target:
return midpoint
#目標值小於當前值,中點值-1爲新的右邊界
elif target < current_item:
right = midpoint-1
else:#否則大於當前值,左側+1爲新的左邊界
left = midpoint+1
return None