查找
1.查找的基本概念
列表:由同一類型的數據元素組成的集合。
關鍵碼:數據元素中的某個數據項,可以標識列表中的一個或一組數據元素。
鍵值:關鍵碼的值。
主關鍵碼:可以唯一地標識一個記錄的關鍵碼。
次關鍵碼:不能唯一地標識一個記錄的關鍵碼。
查找 :在具有相同類型的記錄構成的集合中找出滿足給定條件的記錄。
查找的結果 :若在查找集合中找到了與給定值相匹配的記錄,則稱查找成功;否則,稱查找失敗。
靜態查找 :不涉及插入和刪除操作的查找 。
動態查找 :涉及插入和刪除操作的查找。
靜態查找適用於:查找集合一經生成,便只對其進行查找,而不進行插入和刪除操作; 或經過一段時間的查找之後,集中地進行插入和刪除等修改操作;
動態查找適用於:查找與插入和刪除操作在同一個階段進行,例如當查找成功時,要刪除查找到的記錄,當查找不成功時,要插入被查找的記錄。
查找結構 :面向查找操作的數據結構 ,即查找基於的數據結構。
線性表:適用於靜態查找,主要採用順序查找技術、折半查找技術。
樹表:適用於動態查找,主要採用二叉排序樹的查找技術。
散列表:靜態查找和動態查找均適用,主要採用散列技術。
2.順序查找 (線性查找)
基本思想:
從線性表的一端向另一端逐個將關鍵碼與給定值進行比較,
若相等,則查找成功,給出該記錄在表中的位置;
若整個表檢測完仍未找到與給定值相等的關鍵碼,則查找失敗,給出失敗信息。
int LineSearch :: SeqSearch(int k)
{
i=n;
while (i>0 && data[i]!=k)
i--;
return i;
}
3.順序查找有哨兵
基本思想:設置“哨兵”。
哨兵就是待查值,
將哨兵放在查找方向的盡頭處,
免去了在查找過程中每一次比較後都要判斷查找位置是否越界,從而提高查找速度。
4.順序查找的優點:
算法簡單而且使用面廣。
對錶中記錄的存儲結構沒有任何要求,順序存儲和鏈接存儲均可;
對錶中記錄的有序性也沒有要求,無論記錄是否按關鍵碼有序均可。
5.順序查找的缺點:
平均查找長度較大,特別是當待查找集合中元素較多時,查找效率較低
6.折半查找
適用條件:
線性表中的記錄必須按關鍵碼有序;
必須採用順序存儲。
基本思想:
在有序表中(low, high,low<=high),
取中間記錄作爲比較對象,
若給定值與中間記錄的關鍵碼相等,則查找成功;
若給定值小於中間記錄的關鍵碼,則在中間記錄的左半區繼續查找;
若給定值大於中間記錄的關鍵碼,則在中間記錄的右半區繼續查找。
不斷重複上述過程,直到查找成功,或所查找的區域無記錄,查找失敗。
折半查找判定樹
判定樹:折半查找的過程可以用二叉樹來描述,
樹中的每個結點對應有序表中的一個記錄,
結點的值爲該記錄在表中的位置。
通常稱這個描述折半查找過程的二叉樹爲折半查找判定樹,簡稱判定樹。
⑴ 當n=0時,折半查找判定樹爲空;
⑵ 當n>0時,
折半查找判定樹的根結點爲mid=(n+1)/2,
根結點的左子樹是與有序表r[1] ~ r[mid-1]相對應的折半查找判定樹,
根結點的右子樹是與r[mid+1] ~ r[n]相對應的折半查找判定樹。
判定樹的特點
任意兩棵折半查找判定樹,若它們的結點個數相同,則它們的結構完全相同
具有n個結點的折半查找樹的高度爲
判定樹的性質
任意結點的左右子樹中結點個數最多相差1
任意結點的左右子樹的高度最多相差1
任意兩個葉子所處的層次最多相差1
折半查找性能分析
具有n個結點的折半查找判定樹的深度爲
查找成功:在表中查找任一記錄的過程,即是折半查找判定樹中從根結點到該記錄結點的路徑,和給定值的比較次數等於該記錄結點在樹中的層數。
查找不成功:
查找失敗的過程就是走了一條從根結點到外部結點的路徑,
和給定值進行的關鍵碼的比較次數等於該路徑上內部結點的個數(失敗情況下的平均查找長度等於樹的高度)。
線性表查找的特點
線性表查找是靜態的查找,要在線性表上進行動態查找,存在以下的問題
無序順序表上進行動態查找,插入操作簡單,但查找的複雜性高
有序順序表上進行動態查找,查找的時間複雜性好,但是插入操作時間複雜性高
單鏈表上進行動態查找,插入操作簡單,但查找操作複雜性高
解決辦法:
採用二叉樹這種數據結構,實現動態查找
二叉排序樹(Binary Search Tree)
二叉排序樹(也稱二叉查找樹):或者是一棵空的二叉樹,或者是具有下列性質的二叉樹:
⑴若它的左子樹不空,則左子樹上所有結點的值均小於根結點的值;
⑵若它的右子樹不空,則右子樹上所有結點的值均大於根結點的值;
⑶ 它的左右子樹也都是二叉排序樹。
二叉排序樹的插入
分析:若二叉排序樹爲空樹,則新插入的結點爲新的根結點;否則,新插入的結點必爲一個新的葉子結點,其插入位置由查找過程得到。
若二叉排序樹爲空樹,則新插入的結點爲新的根結點;
否則,如果插入的值比根節點值大,則在右子樹中進行插入;否則,在左子樹中進行插入。
遞歸。
二叉排序樹的構造
從空的二叉排序樹開始,依次插入一個個結點 。
二叉排序樹的刪除
在二叉排序樹上刪除某個結點之後,仍然保持二叉排序樹的特性。
分三種情況討論:
被刪除的結點是葉子;
被刪除的結點只有左子樹或者只有右子樹;
被刪除的結點既有左子樹,也有右子樹。
二叉排序樹的刪除算法——僞代碼
若結點p是葉子,則直接刪除結點p;
2. 若結點p只有左子樹, 則只需重接p的左子樹;
若結點p只有右子樹, 則只需重接p的右子樹;
3. 若結點p的左右子樹均不空,則
3.1 查找結點p的右子樹上的最左下結點s及s雙親結點par;
3.2 將結點s數據域替換到被刪結點p的數據域;
3.3 若結點p的右孩子無左子樹,
則將s的右子樹接到par的右子樹上;
若結點p是葉子,則直接刪除結點p;
2. 若結點p只有左子樹,則只需重接p的左子樹;
若結點p只有右子樹,則只需重接p的右子樹;
3. 若結點p的左右子樹均不空,則
3.1 查找結點p的右子樹上的最左下結點s及s雙親結點par;
3.2 將結點s數據域替換到被刪結點p的數據域;
3.3 若結點p的右孩子無左子樹,
則將s的右子樹接到par的右子樹上;
否則,將s的右子樹接到結點par的左子樹上;
3.4 刪除結點s;
二叉排序樹的查找
在二叉排序樹中查找給定值k的過程是:
⑴ 若root是空樹,則查找失敗;
⑵ 若k=root->data,則查找成功;否則
⑶ 若k<root->data,則在root的左子樹上查找;否則
⑷ 在root的右子樹上查找。
上述過程一直持續到k被找到或者待查找的子樹爲空,如果待查找的子樹爲空,則查找失敗。
二叉排序樹的查找效率在於只需查找二個子樹之一。
平衡二叉樹(AVL樹)
平衡二叉樹:或者是一棵空的二叉排序樹,或者是具有下列性質的二叉排序樹:
⑴ 根結點的左子樹和右子樹的深度最多相差1;
⑵ 根結點的左子樹和右子樹也都是平衡二叉樹。
平衡因子:結點的平衡因子是該結點的左子樹的深度與右子樹的深度之差。
最小不平衡子樹:在平衡二叉樹的構造過程中,以距離插入結點最近的、且平衡因子的絕對值大於1的結點爲根的子樹。
基本思想:
在構造二叉排序樹的過程中,每插入一個結點時,首先檢查是否因插入而破壞了樹的平衡性,
若是,
則找出最小不平衡子樹,
在保持二叉排序樹特性的前提下,調整最小不平衡子樹中各結點之間的鏈接關係,進行相應的旋轉,使之成爲新的平衡子樹。
樹表的查找技術
B-樹
m階B-樹:是滿足下列特性的樹:
樹中每個結點至多有m棵子樹;
(2) 若根結點不是終端結點,則至少有兩棵子樹;
除根結點外,其他非終端結點至少有ém/2ù 棵子樹;
(4)所有非終端結點都包含以下數據:
(n,A0,K1,A1,K2,…,Kn,An)
n(ém/2ù -1≤n≤m -1)爲關鍵碼的個數;
Ki(1≤i≤n)爲關鍵碼,且Ki<Ki+1(1≤i≤n-1);
Ai(0≤i≤n)爲指向子樹根結點的指針,且指針Ai所指子樹中所有結點的關鍵碼均小於Ki+1大於Ki。
(5)所有葉子結點都在同一層上,B樹是高平衡的。
B-樹的插入
基本原理:
當一個節點中插入新的數據時,
會造成節點中數據個數大於(m-1),
此時需要分裂節點,
將節點中第[m/2]+1個數據插入到當前節點的前驅中,
當前節點分裂爲兩個節點。
刪除小結: 在B-樹最下層節點中刪除一個關鍵字
當最下層結點中的關鍵字數大於ém/2ù -1 時,可直接刪除。
當最下層待刪關鍵字所在結點中關鍵字數目爲最低要求ém/2ù -1時,如果其左(右)兄弟中關鍵字數目大於ém/2ù -1,則可採用“父子換位法”。
當最下層待刪結點及其左右兄弟中的關鍵字數目均爲最低要求數目ém/2ù -1時,需要進行合併處理,合併過程與插入時的分裂過程“互逆”,合併一次, 分支數少一,可能出現 “連鎖合併”, 當合併到根時, 各分支深度同時減1。
在B-樹中刪除一個關鍵字-非最下層節點中刪除
基本原則:在保持有序性、平衡性的前提下,進行操作
B+樹的結構定義
m階B+樹的結構定義如下:
(1)每個結點至多有m個子結點;
(2)每個結點(除根外)至少有ceiling(m/2)個子結點;
(3)根結點至少有兩個子結點;
(4)有k個子結點的結點必有k個關鍵碼。
B+樹
m階B+樹:是滿足下列特性的樹:
⑴ 含有m個關鍵碼,每一個關鍵碼對應一棵子樹。
⑵ 關鍵碼Ki是它所對應的子樹的根結點中的最大(或最小)關鍵碼。
⑶ 所有終端結點中包含了全部關鍵碼信息,以及指向關鍵碼記錄的指針。
⑷ 所有終端結點按關鍵碼的大小鏈在一起,形成單鏈表,並設置頭指針。
B+樹的查找
查找應該到葉結點層
在上層已找到待查的關鍵碼,並不停止
而是繼續沿指針向下一直查到葉結點層的這個關鍵碼
B+樹的葉結點一般鏈接起來,形成一個雙鏈表
適合順序檢索(範圍檢索)
B-樹和B+樹
特點
對於階數相同的兩棵樹,每個節點所包含的分支數的定義相同(不能少於m/2,不能多於m)
每個節點所包含的關鍵字的個數不同
B-樹中,關鍵字不重複出現;B+樹中,葉子節點存放所有的關鍵字,內部結點存儲着其後繼節點中最大的關鍵字
插入操作都會引起節點的分裂
刪除操作都會引起節點的合併
B-樹適用於隨機檢索;B+樹支持隨機和順序檢索
散列表(hash)的查找技術
散列函數的構造
直接定址法
除留餘數法
數字分析法
平方取中法
摺疊法(分段疊加法)
衝突處理方法
開放定址法
鏈地址法
建立公共溢出區
散列的基本思想:在記錄的存儲地址和它的關鍵碼之間建立一個確定的對應關係。這樣,不經過比較,一次讀取就能得到所查元素的查找方法。
散列表:採用散列技術將記錄存儲在一塊連續的存儲空間中,這塊連續的存儲空間稱爲散列表。
散列函數:將關鍵碼映射爲散列表中適當存儲位置的函數。
散列地址:由散列函數所得的存儲位置址 。
散列技術一般不適用於允許多個記錄有同樣關鍵碼的情況。
有衝突,降低了查找效率,體現不出計算式查找的優點
散列方法也不適用於範圍查找
不能查找最大值、最小值
也不可能找到在某一範圍內的記錄。
散列技術的關鍵問題:
⑴ 散列函數的設計。如何設計一個簡單、均勻、存儲利用率高的散列函數。
⑵ 衝突的處理。如何採取合適的處理衝突方法來解決衝突。
衝突:對於兩個不同關鍵碼ki≠kj,有H(ki)=H(kj),即兩個不同的記錄需要存放在同一個存儲位置,ki和kj相對於H稱做同義詞。
設計散列函數一般應遵循以下原則:
⑴ 計算簡單。散列函數不應該有很大的計算量,否則會降低查找效率。
⑵ 函數值即散列地址分佈均勻。函數值要儘量均勻散佈在地址空間,這樣才能保證存儲空間的有效利用並減少衝突
散列函數是關鍵碼的線性函數,即:
(key) = a ´ key + b (a,b爲常數)
散列函數爲:
H(key)=key mod p
散列函數——除留餘數法
一般情況下,選p爲小於或等於表長(最好接近表長)的最小素數
散列函數——數字分析法
根據關鍵碼在各個位上的分佈情況,選取分佈比較均勻的若干位組成散列地址。
適用情況:
事先知道關鍵碼的分佈,
關鍵碼的分佈均勻
散列函數——平方取中法
對關鍵碼平方後,按散列表大小,取中間的若干位作爲散列地址(平方後截取)。
適用情況:
事先不知道關鍵碼的分佈且關鍵碼的位數不是很大。
散列函數——摺疊法
將關鍵碼從左到右分割成位數相等的幾部分,將這幾部分疊加求和,取後幾位作爲散列地址。
關鍵碼位數很多,事先不知道關鍵碼的分佈。
處理衝突的方法——開放定址法
由關鍵碼得到的散列地址一旦產生了衝突,就去尋找下一個空的散列地址,並將記錄存入。
如何尋找下一個空的散列地址?
(1)線性探測法
(2)二次探測法
(3)隨機探測法
(4)再hash法
用開放定址法處理衝突得到的散列表叫閉散列表。
線性探測法
當發生衝突時,從衝突位置的下一個位置起,依次尋找空的散列地址。
對於鍵值key,設H(key)=d,閉散列表的長度爲m,則發生衝突時,尋找下一個散列地址的公式爲:
Hi=(H(key)+di) % m (di=1,2,…,m-1)
二次探測法
當發生衝突時,尋找下一個散列地址的公式爲:
Hi=(H(key)+di)% m
(di=12,-12,22,-22,…,q2,-q2且q≤m/2)
隨機探測法
當發生衝突時,下一個散列地址的位移量是一個隨機數列,即尋找下一個散列地址的公式爲:
Hi=(H(key)+di)% m
(di是一個隨機數列,i=1,2,……,m-1)
處理衝突的方法——拉鍊法(鏈地址法)
基本思想:將所有散列地址相同的記錄,即所有同義詞的記錄存儲在一個單鏈表中(稱爲同義詞子表),在散列表中存儲的是所有同義詞子表的頭指針。
用拉鍊法處理衝突構造的散列表叫做開散列表。
處理衝突的方法——公共溢出區
基本思想:
散列表包含基本表和溢出表兩部分(通常溢出表和基本表的大小相同),
將發生衝突的記錄存儲在溢出表中。
查找時,對給定值通過散列函數計算散列地址,先與基本表的相應單元進行比較,若相等,則查找成功;否則,再到溢出表中進行順序查找。