查找

查找一般要掌握順序查找、二分查找、哈希表查找和二叉排序樹查找。要能夠快速準(zhǔn)確地寫出二分查找的代碼。

1. 順序查找

順序查找就是按照順序表,一個(gè)一個(gè)問哎是不是你是不是你,不是就繼續(xù)問下一個(gè)直到找到為止,簡(jiǎn)單有效的方法。

代碼

tips:代碼實(shí)現(xiàn)中有一個(gè)性能優(yōu)化的點(diǎn):設(shè)置一個(gè)哨兵(設(shè)置為數(shù)組首元素a(0)或者末元素a(n-1),將哨兵的值設(shè)置為待查找關(guān)鍵字key的值),一般首元素a(0)用得比較多,然后查找時(shí)從末元素向前找,若找到就返回相應(yīng)下標(biāo);若找不到,則返回首元素的下標(biāo)0,表示查找失敗。

# sequentialSearch.py

def sequentialSearch(inputArray, lengthOfArray, searchedKey):
    inputArray[0] = searchedKey  # 將查找表(即inputArray)的首元素設(shè)置為“哨兵”,并將待查找關(guān)鍵字key的值賦值給“哨兵”
    index = lengthOfArray - 1  # 從查找表的末元素開始向前查找
    while (inputArray[index] != searchedKey):
        index -= 1
    return index # 若查找到符合的位置,則返回相應(yīng)位置的下標(biāo);若返回0,表示查找失敗

if __name__ == "__main__":
    inputArray = [0, 3, 9, 6, 45, 32, 68]
    lengthOfArray = len(inputArray)
    print("lengthOfArray is :", lengthOfArray)
    searchedIndex = sequentialSearch(inputArray, lengthOfArray, 45)
    print("index of key = 45 is :", searchedIndex)
運(yùn)行結(jié)果

這里下標(biāo)從1開始,主要是存儲(chǔ)數(shù)據(jù)從1開始,下標(biāo)為0的位置作它用(這里作為哨兵),依據(jù)數(shù)據(jù)結(jié)構(gòu)而定。

最好時(shí)間復(fù)雜度為O(1) --在第一個(gè)位置就找到;
最壞時(shí)間復(fù)雜度為O(n) --在最后一個(gè)位置才找到;
平均時(shí)間復(fù)雜度為O(n)。

2. 有序表查找

一個(gè)線性表有序時(shí),對(duì)于查找總是很有幫助的。

2.1 二分查找

寫在前面:若要求在排序的數(shù)組(或者部分排序的數(shù)組)中查找一個(gè)數(shù)字或者統(tǒng)計(jì)某個(gè)數(shù)字出現(xiàn)的次數(shù),可嘗試用二分查找算法。

二分查找也稱折半查找(Binary Search),它是一種效率較高的查找方法。

注意:折半查找要求線性表必須采用順序存儲(chǔ)結(jié)構(gòu),而且表中元素按關(guān)鍵字有序排列。順序存儲(chǔ)的原因是:查找過程會(huì)對(duì)下標(biāo)進(jìn)行操作,而鏈?zhǔn)酱鎯?chǔ)只能通過p->next的形式尋找后面的元素,無(wú)法對(duì)下標(biāo)進(jìn)行操作。

查找過程

首先,假設(shè)表中元素是按升序排列,將表中間位置記錄的關(guān)鍵字與查找關(guān)鍵字比較,如果兩者相等,則查找成功;否則利用中間位置記錄將表分成前、后兩個(gè)子表,如果中間位置記錄的關(guān)鍵字大于查找關(guān)鍵字,則進(jìn)一步查找前一子表,否則進(jìn)一步查找后一子表。重復(fù)以上過程,直到找到滿足條件的記錄,使查找成功,或直到子表不存在為止,此時(shí)查找不成功。

算法時(shí)間復(fù)雜度

O(log2^n) #是以2為底,n的對(duì)數(shù)
對(duì)于二分查找的時(shí)間復(fù)雜度,可以將其映射為二叉樹。最多查找次數(shù)不超過二叉樹的深度即log(2 ^ n)+1(對(duì)于n個(gè)結(jié)點(diǎn)的完全二叉樹,其深度為log(2^n)+1)。

代碼
# binarySearch0823 非遞歸實(shí)現(xiàn)

def binarySearch(inputArray, lenOfArray, key):
    # low high mid
    low = 1
    high = lenOfArray
    while low <= high :
        mid = low + (high - low) // 2
        if inputArray[mid] > key:
            high = mid - 1
        elif inputArray[mid] < key:
            low = mid + 1
        else:
            return mid
    return 0

if __name__ == "__main__":
    inputArray = [0, 1, 2, 3, 5, 12, 12, 12, 15, 29, 55] #其中,下標(biāo)為0的位置不參與查找
    lenOfArray = len(inputArray) - 1
    indexOfKey = binarySearch(inputArray, lenOfArray, 15)
    if indexOfKey == 0:
        print("fail to find key!")
    else:
        print("index of key is:", indexOfKey)
程序運(yùn)行結(jié)果
# binarySearch0823_1 遞歸實(shí)現(xiàn)

def binarySearch(inputArray, low, high, key):
    # low high mid
    if low <= high :
        mid = low + (high - low) // 2
        if inputArray[mid] > key:
            return binarySearch(inputArray, low, mid - 1, key)
        elif inputArray[mid] < key:
            return binarySearch(inputArray, mid + 1, high, key)
        else:
            return mid
    return 0

if __name__ == "__main__":
    inputArray = [0, 1, 2, 3, 5, 12, 12, 12, 15, 29, 55] #其中,下標(biāo)為0的位置不參與查找
    lenOfArray = len(inputArray) - 1
    indexOfKey = binarySearch(inputArray, 1, lenOfArray, 15)
    if indexOfKey == 0:
        print("fail to find key!")
    else:
        print("index of key is:", indexOfKey)
程序運(yùn)行結(jié)果

最好時(shí)間復(fù)雜度: O(1)---在第一個(gè)中間位置就找到key
最壞時(shí)間復(fù)雜度:O(log2^n+1) = O(log2^n) ---一直查找到二叉樹的葉子節(jié)點(diǎn)才找到或者遍歷到葉子節(jié)點(diǎn)發(fā)現(xiàn)無(wú)記錄

3. 哈希表查找

無(wú)論是順序表查找,還是二分查找,都需要使用待查找關(guān)鍵字key與數(shù)組元素比較,然后一步步縮小查找范圍,從而查找到key。哈希表查找避免了“比較”的步驟,可以直接通過關(guān)鍵字key得到待查找記錄的內(nèi)存存儲(chǔ)位置(即下標(biāo))。

關(guān)鍵字k與記錄的存儲(chǔ)位置之間的對(duì)應(yīng)關(guān)系稱為散列函數(shù)(又稱哈希函數(shù))。利用散列技術(shù)將記錄存儲(chǔ)在一塊連續(xù)的存儲(chǔ)空間中,這塊連續(xù)存儲(chǔ)空間稱為散列表哈希表。

散列技術(shù)既是一種存儲(chǔ)方法,也是一種查找方法。散列技術(shù)的記錄之間不存在邏輯關(guān)系,記錄只與關(guān)鍵字有關(guān)系。

常用的幾種散列函數(shù)構(gòu)造方法:
1). 直接定址法。散列函數(shù)為線性函數(shù) hash = a * key + b (a, b為常數(shù)) --適合查找表較小且連續(xù)的情況
2). 數(shù)字分析法。抽取關(guān)鍵字的一部分來(lái)計(jì)算散列存儲(chǔ)位置 --適合于關(guān)鍵字位數(shù)比較大的情況
3). 平方取中法。 對(duì)關(guān)鍵字取平方,并抽取中間n位作為散列地址 --適合于不知道關(guān)鍵字分布,且位數(shù)不是很大的情況
4). 折疊法。將關(guān)鍵字從左到右分割成位數(shù)相等的幾部分(若最后面一部分位數(shù)不夠可以與其他位數(shù)不相等),然后將這幾部分疊加求和,并按照散列表表長(zhǎng),取后幾位作為散列地址。 --適合關(guān)鍵字位數(shù)比較大的情況
5). 除留余數(shù)法。f(key) = key mod p (p <= m), m為散列表長(zhǎng)
6). 隨機(jī)數(shù)法。--若關(guān)鍵字長(zhǎng)度不等,可采用此方法

若兩個(gè)關(guān)鍵字key1 != key2, 而通過上面的散列函數(shù)構(gòu)造方法得到的f(key1) == f(key2),即發(fā)生了沖突,此時(shí)key1, key2稱為散列函數(shù)的同義詞,那么可以采用以下方法來(lái)處理沖突。
1). 開放定址法。一旦發(fā)生了沖突,就去尋找下一個(gè)空的散列地址。即:fi(key) = (f(key) + di) mod m (di = 1,2,3,...,m-1); 其中fi(key)是處理沖突后,給關(guān)鍵字key賦予的空散列地址。f(key)是通過上面六種構(gòu)造方法計(jì)算得到的散列地址。 m為散列表表長(zhǎng)。
2). 再散列函數(shù)法。事先準(zhǔn)備多個(gè)散列函數(shù),每當(dāng)有散列地址沖突時(shí),就換一個(gè)散列函數(shù)計(jì)算。
3). 鏈地址法。將所有關(guān)鍵字為同義詞的記錄存儲(chǔ)在一個(gè)單鏈表里,單鏈表稱為同義詞子表,散列表中只存儲(chǔ)所有同義詞子表的頭指針。
4). 公共溢出區(qū)法。為所有沖突的關(guān)鍵字建立一個(gè)公共溢出區(qū)存放。查找時(shí),對(duì)于給定關(guān)鍵字key,先通過散列函數(shù)計(jì)算出散列地址,與基本表相應(yīng)位置比對(duì),若相等則查找成功;若不相等,則到溢出表按順序查找。

代碼
#hashSearch.py

#使用除留余數(shù)法實(shí)現(xiàn)的哈希函數(shù)
def hash(key, hashLen):
    return key % hashLen

#插入關(guān)鍵字進(jìn)散列表
def insertHash(hashTable, hashLen, key):
    hashAddr = hash(key, hashLen) #通過哈希函數(shù)求散列地址
    #如果字典hashTable中的key已經(jīng)存在,說明發(fā)生了沖突,這里采用開放尋址法解決沖突
    while hashTable.get(hashAddr):
        hashAddr += 1
        hashAddr = hash(hashAddr, hashLen) #開放地址法解決沖突:f1(key) = (f(key) + di) % hashLen
    hashTable[hashAddr] = key

#從散列表中查詢關(guān)鍵字key
def searchHash(hashTable, hashLen, key):
    hashAddr = hash(key, hashLen)
    # 若字典hashTable中的key已經(jīng)存在并且key對(duì)應(yīng)的value與待查詢關(guān)鍵字key不相等,說明發(fā)生了沖突,同樣采用開放地址法解決沖突
    while hashTable.get(hashAddr) and hashTable[hashAddr] != key:
        hashAddr += 1
        hashAddr = hash(hashAddr, hashLen)
    if hashTable.get(hashAddr) == None:
        return None
    return hashAddr

if __name__ == "__main__":
    hashLen = 20
    L = [13, 29, 27, 28, 26, 30, 38]
    hashTable = {}
    for i in L:
        insertHash(hashTable, hashLen, i)
    searchResult = searchHash(hashTable, hashLen, 13)
    if searchResult == None:
        print("no this record!")
    else:
        print("index of key is:",searchResult)
程序運(yùn)行結(jié)果,由于13 % 20 = 13,因此,13的散列地址為13

若沒有·沖突,則散列查找時(shí)間復(fù)雜度為O(1). 不管記錄數(shù)n有多大,總可以選擇一個(gè)合適的裝填因子(裝填因子 = 填入表中的記錄個(gè)數(shù)/散列表長(zhǎng)度),將平均查找長(zhǎng)度限定在某一個(gè)范圍內(nèi),此時(shí)散列查找的時(shí)間復(fù)雜度即可一直保持在O(1).

4. 二叉排序樹查找

5. 平衡二叉樹(AVL樹)查找

6. 多路查找樹(B樹)查找

這三個(gè)有關(guān)于樹的查找,參考另一篇文章:完全二叉樹 平衡二叉樹 二叉查找樹 B樹 B+樹

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 一、相關(guān)定義 查找——查找就是根據(jù)給定的某個(gè)值,在查找表中確定一個(gè)其關(guān)鍵字等于給定值的數(shù)據(jù)元素(或記錄)。所有這些...
    開心糖果的夏天閱讀 1,286評(píng)論 0 8
  • 查找 查找表是由同一類型的數(shù)據(jù)元素(或記錄)構(gòu)成的集合。關(guān)鍵字是數(shù)據(jù)元素中某個(gè)數(shù)據(jù)項(xiàng)的值,也稱為鍵值,用它可以標(biāo)識(shí)...
    keeeeeenon閱讀 2,140評(píng)論 0 3
  • 一些定義: 事件的最早發(fā)生時(shí)間(ve(j)):從源點(diǎn)到j(luò)結(jié)點(diǎn)的最長(zhǎng)的路徑。意味著事件最早能夠發(fā)生的時(shí)間。 事件的最...
    北風(fēng)知我意閱讀 805評(píng)論 0 0
  • 本文的整理基于:http://blog.csdn.net/qq_23217629/article/details/...
    阿阿阿阿毛閱讀 1,732評(píng)論 0 3
  • 1.順序查找法以及平均查找長(zhǎng)度(ASL)的計(jì)算; 順序查找是一種最簡(jiǎn)單的查找方法。其基本思想是將查找表作為一個(gè)線性...
    林大飛閱讀 1,332評(píng)論 0 0

友情鏈接更多精彩內(nèi)容