检索
【摘抄】
基于線性表的檢索
一、檢索的基本概念和算法分類
1、檢索概念:
??? 可以形式化地定義基于關(guān)鍵碼的檢索。假定k1、k2…kn是互不相同的關(guān)鍵碼值,有一個(gè)包含n條記錄的集合C,形式如下: (k1, R1),(k2, R2),…,(kn, Rn) 其中Rj是與關(guān)鍵碼kj相關(guān)聯(lián)的信息。給定某個(gè)關(guān)鍵碼值K,檢索問題( search problem )就是在C中定位記錄(kj, Rj) ,使得kj = K。檢索( searching )就是定位關(guān)鍵碼值kj = K的記錄的系統(tǒng)過程。
2、檢索算法分類:
(1) 基于線性表的檢索。例如,順序檢索、二分檢索。
(2) 根據(jù)關(guān)鍵碼值直接訪問。例如,根據(jù)數(shù)組下標(biāo)的直接檢索、散列檢索。
(3) 樹索引方法。例如,二叉搜索樹、字符樹、B樹。
(4) 基于屬性的檢索。例如,倒排表、倒排文件。
二、衡量檢索算法
??? 評價(jià)一個(gè)檢索算法的效率,需要在時(shí)間和空間兩方面進(jìn)行權(quán)衡。檢索運(yùn)算的主要操作是關(guān)鍵碼值的比較,我們通常把檢索過程中對關(guān)鍵碼需要執(zhí)行的平均比較次數(shù),稱為平均檢索長度(Average Search Length),它是衡量檢索算法優(yōu)劣的時(shí)間標(biāo)準(zhǔn)。顯然平均檢索長度是存儲(chǔ)結(jié)構(gòu)中對象總數(shù)n的函數(shù),其定義為:
??? 其中,Pi為檢索第i個(gè)元素(即給定值K與存儲(chǔ)結(jié)構(gòu)中第i個(gè)元素的關(guān)鍵碼值相等)的概率,Ci為找到第i個(gè)元素所需的關(guān)鍵碼值與給定值的比較次數(shù)。假設(shè)線性表為(a, b, c),檢索a、b、c的概率分別為0.4、0.1、0.5,則順序檢索算法的平均檢索長度為0.4×1+0.1×2+0.5×3 = 2.1,即平均需要2.1次給定值與表中關(guān)鍵碼值的比較才能找到待查元素。此外,我們還需要考慮不成功的檢索。
??? “檢索第i個(gè)元素的概率P”可理解為:在很多次的檢索中,找第i個(gè)元素的次數(shù)占總次數(shù)的比例為P。顯然,若已知各個(gè)元素檢索概率的分布情況,則ASL可準(zhǔn)確地反映檢索算法的平均時(shí)間性能。
??? 另外,衡量一個(gè)檢索算法還要考慮算法所需的存儲(chǔ)量、算法的復(fù)雜性等因素。
三、順序檢索
??? 順序檢索的算法思想是:針對線性表里的所有記錄,逐個(gè)進(jìn)行關(guān)鍵碼和給定值的比較,若某個(gè)記錄的關(guān)鍵碼和給定值比較相等,則檢索成功,找到所查記錄;反之,檢索失敗。表中各數(shù)據(jù)元素之間不必?fù)碛羞壿嬯P(guān)系,即它們在表中可以任意排列。
??? 與其它檢索方法相比,順序檢索對表的特性沒有要求,數(shù)據(jù)元素可以任意排列。插入元素可以直接加到表尾,時(shí)間代價(jià)為Θ(1)。這是其主要優(yōu)點(diǎn)。但順序檢索的平均檢索長度較大,在平均和最差的情況下的時(shí)間代價(jià)都是Θ(n)。當(dāng)數(shù)據(jù)規(guī)模較大時(shí),檢索效率比較低。?
???
四、二分檢索和分治算法
??? 二分檢索是針對有序表的檢索。所謂有序表,是指線性表中的所有數(shù)據(jù)元素按關(guān)鍵碼值的某種次序進(jìn)行遞增或遞降的排列。二分檢索法的基本思想是:每次將待查區(qū)間中間位置上的數(shù)據(jù)元素的關(guān)鍵碼值與給定值K比較,若不等則縮小檢索區(qū)間并在新的區(qū)間內(nèi)重復(fù)上述過程,直到檢索成功或檢索區(qū)間長度為0(檢索不成功)為止。
??? 二分檢索法的效率可以通過二分檢索的決策樹進(jìn)行衡量。其平均檢索長度與最大檢索長度相近,效率較高。但它要求被檢索序列事先按關(guān)鍵碼的次序(遞增或遞減)排列,而排序本身是一種很費(fèi)時(shí)的運(yùn)算;另外,二分檢索只適用于順序存儲(chǔ)結(jié)構(gòu),而在順序結(jié)構(gòu)中插入和刪除都比較困難。因此,二分檢索特別適用于那種一經(jīng)建立就很少改動(dòng)、而又需要經(jīng)常檢索的線性表。
??? 現(xiàn)以有序表(15,17,18,22,35,51,60,88,93)為例說明用二分檢索算法查找K=18的過程(這里假定表中各元素只含關(guān)鍵碼)。首先,取整個(gè)有序表為檢索區(qū)間,這通過分別置low、high為1、9來完成。
??? 因區(qū)間長度大于0,取區(qū)間中間位置mid = (1+9)/2 = 5,將mid位置上元素的關(guān)鍵碼值35與K=18比較。因18<35,將區(qū)間縮小為[1,4]。注意此新區(qū)間與原區(qū)間[1,9]的差別僅在于上界不同,修改區(qū)間的工作可通過修改上界high = mid-1=5-1完成。
??? 由于循環(huán)終止條件未滿足,重復(fù)上述過程。這時(shí)區(qū)間中點(diǎn)為mid=(1+4)/2=2,比較結(jié)果18>17表明區(qū)間應(yīng)改為[3,4],這一修改由下界的修改low = mid+1=2+1完成。
??? 再次進(jìn)行比較時(shí),區(qū)間中點(diǎn)為mid=(3+4)/2=3,比較結(jié)果表明dataList[mid]正是待查元素,檢索成功,返回結(jié)果為mid=3。(如下圖所示)
二分法檢索演示圖
<圖>
分治法思想:
??? 一般而言,計(jì)算機(jī)求解問題的規(guī)模越小,所需的計(jì)算時(shí)間也越少。對于規(guī)模為N的問題,分治策略將其分解為k(k = 2, 3, 4, …,一般取k=2)個(gè)相同類型的子問題,每個(gè)子問題的規(guī)模相對較小且相互獨(dú)立;遞歸地求解這些子問題,并將所得結(jié)果合并而求出原來問題的解。這就是分治策略的基本思想。
??? 分治策略算法通常分為三個(gè)部分:分割、求解、合并。
五、分塊檢索
??? 分塊檢索又稱為索引檢索,性能介于順序檢索和二分檢索之間。它把線性表分成若干塊,在每一塊中結(jié)點(diǎn)的存放是任意的,但是塊與塊之間必須保持關(guān)鍵碼值遞增(或者遞減)的順序。把(每塊中最大的關(guān)鍵碼值,塊的起始位置)這樣的二元組構(gòu)成一個(gè)索引表。由于表是分塊有序的,所以索引表是一個(gè)遞增(遞減)有序表。檢索時(shí),首先用待檢索的關(guān)鍵碼在索引中查找,確定如果滿足條件的結(jié)點(diǎn)存在時(shí)它應(yīng)在哪一塊中,在索引中檢索的方法既可以采用二分法、也可以采用順序檢索;然后再到相應(yīng)的塊中順序檢索,便可以得到檢索的結(jié)果。
??? 分塊檢索的主要代價(jià)是增加一個(gè)輔助索引數(shù)組的存儲(chǔ)空間和將初始線性表分塊排序的運(yùn)算。另外當(dāng)大量的插入刪除運(yùn)算使塊中結(jié)點(diǎn)數(shù)分布很不均勻時(shí),檢索速度將會(huì)下降。
??? 分塊檢索的優(yōu)點(diǎn)是:在線性表中插入或刪除一個(gè)結(jié)點(diǎn)時(shí),只要找到該結(jié)點(diǎn)應(yīng)屬于的塊,然后在塊內(nèi)進(jìn)行插入和刪除運(yùn)算。由于塊內(nèi)結(jié)點(diǎn)的存放是任意的,所以插入或刪除比較容易,不需要移動(dòng)大量的結(jié)點(diǎn)。插入可以在塊尾進(jìn)行;如果待刪除的記錄不是塊中最后一個(gè)記錄時(shí),可以將本塊內(nèi)最后一個(gè)記錄移入被刪除記錄的位置。
??? 總體來說:順序檢索效率最低但限制最少。二分檢索效率最高但限制多。而分塊檢索則介于上述二者之間,在實(shí)際應(yīng)用中,可根據(jù)表的具體情況進(jìn)行選擇,需要綜合考慮檢索效率、插入刪除頻率等。
???
集合的檢索
一、集合特性
??? 集合是由若干個(gè)確定的、相異的對象構(gòu)成的。這些對象稱元素,一個(gè)集合中不包含兩個(gè)完全相同的元素。在問題求解中,集合是十分有用的工具。
??? 元素個(gè)數(shù)為零的集合稱為“空集”,一般用φ來表示。
??? 最基本的關(guān)系是成員關(guān)系,若x是集合A的元素,則稱“x屬于A”,記作x∈A。
??? 設(shè)有兩個(gè)集合A和B,如果集合A的每個(gè)元素也都是集合B的元素,稱集合A被集合B包含,也稱A是B的子集,或稱B是A的“超集”(superset)。如果A、B兩個(gè)集合互相包含,則稱這兩個(gè)集合相等,記作A=B。集合A是集合B的一個(gè)真子集(或真超集),必須滿足A是B的子集,并且A≠B。
??? 集合最基本的運(yùn)算是并、交、差。由至少屬于集合A和集合B之一的一切元素組成的集合,稱為A和B的并集,記作A∪B。由集合A和集合B的所有共同元素所組成的集合,稱為A和B的交集,記作A∩B。由所有屬于A但不屬于B的元素的全體所組成的集合,稱為A和B的差集,記作A-B。
??? 計(jì)算機(jī)所支持的集合的基類型(basetype),一般是有限、順序類型。被定義的集合類型稱為與基類型相聯(lián)系的集合類型。集合類型的值集是其基類型值集的冪集。集合類型的每個(gè)值是其基類型值集的一個(gè)子集。
??? 與集合有關(guān)的運(yùn)算可以定義為: ???
???
二、位圖檢索
??? 要判斷某一元素是否在數(shù)組中,即集合中的“IN”運(yùn)算,是在一組記錄中檢索關(guān)鍵碼的一種特殊情況。本書所討論的所有檢索方法都可以完成這個(gè)任務(wù)。
??? 在關(guān)鍵碼值范圍有限的情況下,可以采用一種簡單的技術(shù),這就是存儲(chǔ)一個(gè)位數(shù)組(bit arrary),為每一個(gè)可能的元素分配一個(gè)比特位位置。如果元素確實(shí)包含在實(shí)際集合中,就把它對應(yīng)的位設(shè)置為1;如果元素不包含在集合中,就把它對應(yīng)的位設(shè)置為0。Pascal語言能夠直接支持集合類型,其集合類型就是用一個(gè)位數(shù)組來實(shí)現(xiàn)的。
??? 例如對于字符型集合為(小寫字母['a'..'z']),而集合型變量chset = ['a','c','h','i',j','m','n',t','v','w,'y'],那么對應(yīng)于變量chset的位數(shù)組為:
<圖>
??? 這種表示方法很省空間,而且對于“屬于”、“并”、“交”和“差”(“IN”、“+”、“*”和“-”)操作十分方便。集合比數(shù)組的操作更加便捷。例如,對于數(shù)組的插入和刪除,都有大量的數(shù)據(jù)移動(dòng);而集合類型的“并”、“交”和“差”運(yùn)算只需要在修改相應(yīng)的比特位標(biāo)記。要確定某個(gè)元素是否在集合中,只需要直接檢查對應(yīng)的位標(biāo)志。這種表示方法稱為位向量( bit vector )或者位圖( bitmap )。
??? 如果集合大小在計(jì)算機(jī)的一個(gè)字長范圍內(nèi),而且高級語言支持按位操作,就可以通過邏輯的位操作而完成集合的并、交、差運(yùn)算。例如,在C++ 語言中,集合A和B的并運(yùn)算就是“A | B”(按位或),集合的交運(yùn)算就是“A & B”(按位與),集合A與B的差運(yùn)算可以使用表達(dá)式“A &~ B”( ~是非運(yùn)算的符號)實(shí)現(xiàn)。例如,如果要計(jì)算數(shù)字0到數(shù)字15之間奇素?cái)?shù)集合,只需要計(jì)算表達(dá)式
??? 0011010100010100 & 0101010101010101
??? 得到結(jié)果“0001010100010100”,表示0到15之間的奇素?cái)?shù)集合為{3,5,7,11,13}。
???
總結(jié):在信息檢索( document retrieval )中,有一種簽名文件( signature file )技術(shù)就是根據(jù)位向量來計(jì)算待檢索的文檔集合的。???
散列方法
??? 前面我們所介紹的檢索,基本上都是基于關(guān)鍵碼比較的檢索。例如,順序檢索和分塊檢索依賴于“等于”(“==”)或者“不等于”(“!=”)的判斷,而二分檢索和樹型檢索(BST,B樹等)依賴于 “大于”(“>”)、“等于”(“==”)“>”、“小于”( “<”)這三種判斷。這些檢索方法的平均檢索長度都與n有關(guān)。檢索是直接面向用戶的操作,當(dāng)問題規(guī)模n很大時(shí),上述檢索的時(shí)間效率可能使得用戶無法忍受。
??? 最理想的情況是,根據(jù)關(guān)鍵碼值,直接找到記錄的存儲(chǔ)地址,而不需要把待查關(guān)鍵碼與候選記錄集合的某些記錄進(jìn)行逐個(gè)比較。 計(jì)算機(jī)科學(xué)家發(fā)明了散列的方法。本節(jié)則主要討論散列檢索技術(shù),包括各種散列函數(shù)和解決散列沖突的方法等。
???
一、散列基本概念
??? 散列方法的主要思想是根據(jù)結(jié)點(diǎn)的關(guān)鍵碼值來確定其存儲(chǔ)地址:以關(guān)鍵碼值K為自變量,通過一定的函數(shù)關(guān)系h(K)(稱為散列函數(shù)),計(jì)算出對應(yīng)的函數(shù)值來,把這個(gè)值解釋為結(jié)點(diǎn)的存儲(chǔ)地址,將結(jié)點(diǎn)存入到此存儲(chǔ)單元中。檢索時(shí),用同樣的方法計(jì)算地址,然后到相應(yīng)的單元里去取要找的結(jié)點(diǎn)。通過散列方法可以對結(jié)點(diǎn)進(jìn)行快速檢索。散列(hash,也稱“哈?!?#xff09;是一種重要的存儲(chǔ)方式,也是一種常見的檢索方法。
??? 按散列存儲(chǔ)方式構(gòu)造的存儲(chǔ)結(jié)構(gòu)稱為散列表(hash table)。散列表中的一個(gè)位置稱為槽(slot)。散列技術(shù)的核心是散列函數(shù)(hash function)。
??? 對任意給定的動(dòng)態(tài)查找表DL,如果選定了某個(gè)“理想的”散列函數(shù)h及相應(yīng)的散列表HT,則對DL中的每個(gè)數(shù)據(jù)元素X。函數(shù)值 h(X.key)就是X在散列表HT中的存儲(chǔ)位置。插入(或建表)時(shí)數(shù)據(jù)元素X將被安置在該位置上,并且檢索X時(shí)也到該位置上去查找。由散列函數(shù)決定的存儲(chǔ)位置稱為散列地址。
??? 因此,散列的核心就是:由散列函數(shù)決定關(guān)鍵碼值(X.key)與散列地址h(X.key)之間的對應(yīng)關(guān)系,通過這種關(guān)系來實(shí)現(xiàn)組織存儲(chǔ)并進(jìn)行檢索。
??? 一般情況下,散列表的存儲(chǔ)空間是一個(gè)一維數(shù)組HT[M],散列地址是數(shù)組的下標(biāo)。設(shè)計(jì)散列方法的目標(biāo),就是設(shè)計(jì)某個(gè)散列函數(shù)h,0<=h( K ) < M;對于關(guān)鍵碼值K,得到HT[i] = K。
??? 在一般情況下,散列表的空間必須比結(jié)點(diǎn)的集合大,此時(shí)雖然浪費(fèi)了一定的空間,但換取的是檢索效率。設(shè)散列表的空間大小為M,填入表中的結(jié)點(diǎn)數(shù)為N,則稱 為散列表的負(fù)載因子(load factor,也有人翻譯為“裝填因子”)。建立散列表時(shí),若關(guān)鍵碼與散列地址是一對一的關(guān)系,則在檢索時(shí)只需根據(jù)散列函數(shù)對給定值進(jìn)行某種運(yùn)算,即可得到待查結(jié)點(diǎn)的存儲(chǔ)位置。但是,散列函數(shù)可能對于不相等的關(guān)鍵碼計(jì)算出相同的散列地址,我們稱該現(xiàn)象為沖突(collision),發(fā)生沖突的兩個(gè)關(guān)鍵碼稱為該散列函數(shù)的同義詞。在實(shí)際應(yīng)用中,很少存在不產(chǎn)生沖突的散列函數(shù),我們必須考慮在沖突發(fā)生時(shí)的處理辦法。
??? 因此,采用散列技術(shù)時(shí)需要考慮的兩個(gè)首要問題是:
??? (1)如何構(gòu)造(選擇)使結(jié)點(diǎn)“分布均勻”的散列函數(shù)?
??? (2)一旦發(fā)生沖突,用什么方法來解決?
??? 當(dāng)然,還需考慮散列表本身的組織方法。下面分別加以討論。
二、散列函數(shù)
??? 本節(jié)討論幾種散列函數(shù)。在以下的討論中,我們假設(shè)處理的是值為整型的關(guān)鍵碼,否則我們總可以建立一種關(guān)鍵碼與正整數(shù)之間的一一對應(yīng)關(guān)系,從而把該關(guān)鍵碼的檢索轉(zhuǎn)化為對與其對應(yīng)的正整數(shù)的檢索;同時(shí),進(jìn)一步假定散列函數(shù)的值落在0到M-1之間。散列函數(shù)的選取原則是:運(yùn)算盡可能簡單;函數(shù)的值域必須在散列表的范圍內(nèi);盡可能使得結(jié)點(diǎn)均勻分布,也就是盡量讓不同的關(guān)鍵碼具有不同的散列函數(shù)值。需要考慮各種因素:關(guān)鍵碼長度、散列表大小、關(guān)鍵碼分布情況、記錄的檢索頻率等等。下面我們介紹幾種常用的散列函數(shù)。
??? 1、除余法
顧名思義,除余法就是用關(guān)鍵碼x除以M(往往取散列表長度),并取余數(shù)作為散列地址。除余法幾乎是最簡單的散列方法,散列函數(shù)為: h(x) = x mod M。
??? 2、乘余取整法
使用此方法時(shí),先讓關(guān)鍵碼key乘上一個(gè)常數(shù)A (0< A < 1),提取乘積的小數(shù)部分。然后,再用整數(shù)n乘以這個(gè)值,對結(jié)果向下取整,把它做為散列的地址。散列函數(shù)為: hash ( key ) = _LOW( n × ( A × key % 1 ) )。
其中,“A × key % 1”表示取 A × key 小數(shù)部分,即: A × key % 1 = A × key - _LOW(A × key), 而_LOW(X)是表示對X取下整。
??? 3、平方取中法
由于整數(shù)相除的運(yùn)行速度通常比相乘要慢,所以有意識(shí)地避免使用除余法運(yùn)算可以提高散列算法的運(yùn)行時(shí)間。平方取中法的具體實(shí)現(xiàn)是:先通過求關(guān)鍵碼的平方值,從而擴(kuò)大相近數(shù)的差別,然后根據(jù)表長度取中間的幾位數(shù)(往往取二進(jìn)制的比特位)作為散列函數(shù)值。因?yàn)橐粋€(gè)乘積的中間幾位數(shù)與乘數(shù)的每一數(shù)位都相關(guān),所以由此產(chǎn)生的散列地址較為均勻。
??? 4、數(shù)字分析法
設(shè)有 n 個(gè) d 位數(shù),每一位可能有 r 種不同的符號。這 r 種不同的符號在各位上出現(xiàn)的頻率不一定相同,可能在某些位上分布均勻些,每種符號出現(xiàn)的幾率均等; 在某些位上分布不均勻,只有某幾種符號經(jīng)常出現(xiàn)??筛鶕?jù)散列表的大小,選取其中各種符號分布均勻的若干位作為散列地址。
??? 5、基數(shù)轉(zhuǎn)換法
將關(guān)鍵碼值看成另一種進(jìn)制的數(shù)再轉(zhuǎn)換成原來進(jìn)制的數(shù),然后選其中幾位作為散列地址。
??? 6、折疊法
有時(shí)關(guān)鍵碼所含的位數(shù)很多,采用平方取中法計(jì)算太復(fù)雜,則可將關(guān)鍵碼分割成位數(shù)相同的幾部分(最后一部分的位數(shù)可以不同),然后取這幾部分的疊加和(舍去進(jìn)位)作為散列地址,這方法稱為折疊法。
??? 7、ELFhash字符串散列函數(shù)
ELFhash函數(shù)在UNIX系統(tǒng)V 版本4中的“可執(zhí)行鏈接格式”( Executable and Linking Format,即ELF )中會(huì)用到,ELF文件格式用于存儲(chǔ)可執(zhí)行文件與目標(biāo)文件。ELFhash函數(shù)是對字符串的散列。它對于長字符串和短字符串都很有效,字符串中每個(gè)字符都有同樣的作用,它巧妙地對字符的ASCII編碼值進(jìn)行計(jì)算,ELFhash函數(shù)對于能夠比較均勻地把字符串分布在散列表中。
三、沖突解決策略
??? 盡管散列函數(shù)的目標(biāo)是使得沖突最少,但實(shí)際上沖突是無法避免的。因此,我們必須研究沖突解決策略。
??? 沖突解決技術(shù)可以分為兩類:開散列方法( open hashing,也稱為拉鏈法,separate chaining )和閉散列方法( closed hashing,也稱為開地址方法,open addressing )。這兩種方法的不同之處在于:開散列法把發(fā)生沖突的關(guān)鍵碼存儲(chǔ)在散列表主表之外,而閉散列法把發(fā)生沖突的關(guān)鍵碼存儲(chǔ)在表中另一個(gè)槽內(nèi)。
1、開散列方法。
沖突解決策略/開散列方法
??? <1>、拉鏈法
??? 開散列方法的一種簡單形式是把散列表中的每個(gè)槽定義為一個(gè)鏈表的表頭。散列到一個(gè)特定槽的所有記錄都放到這個(gè)槽的鏈表中。圖9-5說明了一個(gè)開散列的散列表,這個(gè)表中每一個(gè)槽存儲(chǔ)一個(gè)記錄和一個(gè)指向鏈表其余部分的指針。這7個(gè)數(shù)存儲(chǔ)在有11個(gè)槽的散列表中,使用的散列函數(shù)是h(K) = K mod 11。數(shù)的插入順序是77、7、110、95、14、75和62。有2個(gè)值散列到第0個(gè)槽,1個(gè)值散列到第3個(gè)槽,3個(gè)值散列到第7個(gè)槽,1個(gè)值散列到第 9個(gè)槽。
??? <2>、桶式散列
??? 桶式散列方法的基本思想是把一個(gè)文件的記錄分為若干存儲(chǔ)桶,每個(gè)存儲(chǔ)桶包含一個(gè)或多個(gè)頁塊,一個(gè)存儲(chǔ)桶內(nèi)的各頁塊用指針連接起來,每個(gè)頁塊包含若干記錄。散列函數(shù)h把關(guān)鍵碼值K轉(zhuǎn)換為存儲(chǔ)桶號,即h(K)表示具有關(guān)鍵碼值K的記錄所在的存儲(chǔ)桶號。
??? 圖9-6表示了一個(gè)具有B個(gè)存儲(chǔ)桶的散列文件組織。有一個(gè)存儲(chǔ)桶目錄表,存放B個(gè)指針,每個(gè)存儲(chǔ)桶一個(gè),每個(gè)指針就是所對應(yīng)存儲(chǔ)桶的第一個(gè)頁塊的地址。
??? 有些存儲(chǔ)桶僅僅由一個(gè)頁塊組成,如下圖中的1號存儲(chǔ)桶。有的存儲(chǔ)桶由多個(gè)頁塊組成,每一個(gè)頁塊的塊頭上有一個(gè)指向下一個(gè)頁塊的指針,例如,如下圖中的第B-1號存儲(chǔ)桶由b4,b5,b6三個(gè)頁塊組成,每個(gè)存儲(chǔ)桶中最后一個(gè)頁塊的頭上為空指針。
2、閉散列方法。
沖突解決策略/閉散列方法
??? 閉散列方法把所有記錄直接存儲(chǔ)在散列表中。每個(gè)記錄關(guān)鍵碼key有一個(gè)由散列函數(shù)計(jì)算出來的基位置,即h(key)。如果要插入一個(gè)關(guān)鍵碼,而另一個(gè)記錄已經(jīng)占據(jù)了R的基位置(發(fā)生碰撞),那么就把R存儲(chǔ)在表中的其它地址內(nèi),由沖突解決策略確定是哪個(gè)地址。
??? 閉散列表解決沖突的基本思想是:當(dāng)沖突發(fā)生時(shí),使用某種方法為關(guān)鍵碼K生成一個(gè)散列地址序列d0,d1,d2,... di ,...dm-1。其中d0=h(K)稱為K的基地址地置( home position );所有di(0< i< m)是后繼散列地址。當(dāng)插入K時(shí),若基地址上的結(jié)點(diǎn)已被別的數(shù)據(jù)元素占用,則按上述地址序列依次探查,將找到的第一個(gè)開放的空閑位置di作為K的存儲(chǔ)位置;若所有后繼散列地址都不空閑,說明該閉散列表已滿,報(bào)告溢出。相應(yīng)地,檢索K時(shí),將按同值的后繼地址序列依次查找,檢索成功時(shí)返回該位置di ;如果沿著探查序列檢索時(shí),遇到了開放的空閑地址,則說明表中沒有待查的關(guān)鍵碼。刪除K時(shí),也按同值的后繼地址序列依次查找,查找到某個(gè)位置di具有該K 值,則刪除該位置di上的數(shù)據(jù)元素(刪除操作實(shí)際上只是對該結(jié)點(diǎn)加以刪除標(biāo)記);如果遇到了開放的空閑地址,則說明表中沒有待刪除的關(guān)鍵碼。因此,對于閉散列表來說,構(gòu)造后繼散列地址序列的方法,也就是處理沖突的方法。
??? 形成探查的方法不同,所得到的解決沖突的方法也不同。下面是幾種常見的構(gòu)造方法。
??? <1>、線性探查法
??? 將散列表看成是一個(gè)環(huán)形表,若在基地址d(即h(K)=d)發(fā)生沖突,則依次探查下述地址單元:d+1,d+2,......,M- 1,0,1,......,d-1直到找到一個(gè)空閑地址或查找到關(guān)鍵碼為key的結(jié)點(diǎn)為止。當(dāng)然,若沿著該探查序列檢索一遍之后,又回到了地址d,則無論是做插入操作還是做檢索操作,都意味著失敗。
??? 用于簡單線性探查的探查函數(shù)是: p(K,i) = i
??? 例9.7 已知一組關(guān)鍵碼為(26,36,41,38,44,15,68,12,06,51,25),散列表長度M= 15,用線性探查法解決沖突構(gòu)造這組關(guān)鍵碼的散列表。
??? 因?yàn)閚=11,利用除余法構(gòu)造散列函數(shù),選取小于M的最大質(zhì)數(shù)P=13,則散列函數(shù)為:h(key) = key%13。按順序插入各個(gè)結(jié)點(diǎn): 26: h(26) = 0,36: h(36) = 10, 41: h(41) = 2,38: h(38) = 12, 44: h(44) = 5。
??? 插入15時(shí),其散列地址為2,由于2已被關(guān)鍵碼為41的元素占用,故需進(jìn)行探查。按順序探查法,顯然3為開放的空閑地址,故可將其放在3單元。類似地,68和12可分別放在4和13單元中,下圖顯示了插入15和68時(shí)的過程。
??
??? <2>、二次探查法
??? 二次探查法的基本思想是:生成的后繼散列地址不是連續(xù)的,而是跳躍式的,以便為后續(xù)數(shù)據(jù)元素留下空間從而減少聚集。二次探查法的探查序列依次為:12,-12,22 ,-22,...等,也就是說,發(fā)生沖突時(shí),將同義詞來回散列在第一個(gè)地址的兩端。求下一個(gè)開放地址的公式為:
???
??? <3>、隨機(jī)探查法
??? 理想的探查函數(shù)應(yīng)當(dāng)在探查序列中隨機(jī)地從未訪問過的槽中選擇下一個(gè)位置,即探查序列應(yīng)當(dāng)是散列表位置的一個(gè)隨機(jī)排列。但是,我們實(shí)際上不能隨機(jī)地從探查序列中選擇一個(gè)位置,因?yàn)樵跈z索關(guān)鍵碼的時(shí)候不能建立起同樣的探查序列。然而,我們可以做一些類似于偽隨機(jī)探查( pseudo-random probing )的事情。在偽隨機(jī)探查中,探查序列中的第i個(gè)槽是(h(K) + ri) mod M,其中ri是1到M - 1之間數(shù)的“隨機(jī)”數(shù)序列。所有插入和檢索都使用相同的“隨機(jī)”數(shù)。探查函數(shù)將是 p(K,i) = perm[i - 1],這里perm是一個(gè)長度為M - 1的數(shù)組,它包含值從1到M – 1的隨機(jī)序列。
??? <4>、雙散列探查法
??? 偽隨機(jī)探查和二次探查都能消除基本聚集——即基地址不同的關(guān)鍵碼,其探查序列的某些段重疊在一起——的問題。然而,如果兩個(gè)關(guān)鍵碼散列到同一個(gè)基地址,那么采用這兩種方法還是得到同樣的探查序列,仍然會(huì)產(chǎn)生聚集。這是因?yàn)閭坞S機(jī)探查和二次探查產(chǎn)生的探查序列只是基地址的函數(shù),而不是原來關(guān)鍵碼值的函數(shù)。這個(gè)問題稱為二級聚集( secondary clustering )。
??? 為了避免二級聚集,我們需要使得探查序列是原來關(guān)鍵碼值的函數(shù),而不是基位置的函數(shù)。雙散列探查法利用第二個(gè)散列函數(shù)作為常數(shù),每次跳過常數(shù)項(xiàng),做線性探查。
四、散列檢索效率分析
??? 我們可以根據(jù)完成一次操作,即插入、刪除和檢索操作,所需要的記錄訪問次數(shù)來衡量散列方法的性能。由于散列表的插入和刪除操作都是基于檢索進(jìn)行的:在刪除一條記錄之前必須先找到該記錄,因此刪除一條記錄之前需要的訪問數(shù)等于成功檢索到它需要的訪問數(shù);而插入一條記錄時(shí),必須找到探查序列的尾部(對于不考慮刪除的情況,是尾部的空槽;對于考慮刪除的情況,也要找到尾部,才能確定是否有重復(fù)記錄),這等于對這條記錄進(jìn)行一次不成功的檢索。因此,散列表的效率實(shí)質(zhì)上還是平均檢索長度,而且我們需要區(qū)別對待成功的檢索與不成功的檢索。
??? 當(dāng)散列表比較空的時(shí)候,所插入的記錄比較容易插入到其空閑的基地址。如果散列表中的記錄比較多,插入記錄時(shí),很可能要靠沖突解決策略來尋找探查序列中合適的另一個(gè)槽。而且,檢索記錄時(shí),很多時(shí)候需要沿著探查序列逐個(gè)查找。隨著散列表記錄不斷增加,越來越多的記錄有可能放到離其基地址更遠(yuǎn)的地方。
??? 根據(jù)這些討論,我們可以看到散列方法預(yù)期的代價(jià)與負(fù)載因子α= N/M有關(guān)。其中,M是散列表存儲(chǔ)空間大小,N是表中當(dāng)前的記錄數(shù)目。
??? 從圖9-8可以看出,開散列方法的效率最好,實(shí)際系統(tǒng)中使用的散列大多都是開散列。開散列方法非常簡單、易于實(shí)現(xiàn),它不會(huì)產(chǎn)生聚集現(xiàn)象(聚集導(dǎo)致更大的平均檢索長度),刪除也極為方便。大部分?jǐn)?shù)據(jù)結(jié)構(gòu)教材用比較多的篇幅來討論閉散列方法,是因?yàn)殚]散列需要考慮的因素更多,因而更需要精心設(shè)計(jì),閉散列在某些受限制的系統(tǒng)中(例如不能使用堆棧分配新空間)有獨(dú)到的用途。并且,經(jīng)過精心設(shè)計(jì)的閉散列的效率比開散列穩(wěn)定。?
??
??
總結(jié):
散列法的平均檢索長度不隨表目數(shù)量的增加而增加,而是隨負(fù)載因子的增大而增加。如果安排得好,平均檢索長度可以小于1.5。正是由于這個(gè)特性,散列法成為一種很受歡迎的高效檢索方法。例如,搜索引擎中關(guān)鍵詞字典、域名服務(wù)器DNS中域名與IP地址的對應(yīng)(例如,db.pku.edu.cn與 162.105.203.98,www.google.com與216.239.53.99)、操作系統(tǒng)中命令路徑下的所有可執(zhí)行程序名、編譯系統(tǒng)中的符號表等,都采用了散列技術(shù)以提高查找速度。??
來源:http://www.jpk.pku.edu.cn/pkujpk/course/sjjg/chapter9/01/t01_jj.html
轉(zhuǎn)載于:https://www.cnblogs.com/GoGoagg/archive/2011/03/08/1977361.html
總結(jié)
- 上一篇: Linux下Web效力器架设攻略-1
- 下一篇: 获取Dataset前几条数据的两种方法