python實現各種常用算法之搜索算法(12)

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章