從現在開始我的博客講介紹有關計算機基礎之數據結構的內容,我將會把核心的內容講解出來,希望能給大家帶來幫助。
一:查找的基本概念
查找是在集合中尋找滿足某種條件的數據元素的過程,分為查找成功和查找失敗。
用於查找的數據元素集合稱為查找表,由同一類型的數據元素組成,可以是數組或者鏈表。可以根據是否要動態修改查找表才能查找到元素分為靜態查找表和動態查找表。
關鍵字是數據元素中唯一標識某元素的某個數據項的值,使用基於關鍵字的查找,查找結果是唯一的。
查找效率需要有一個評判標准,最通用的方法是用平均查找長度ASL評判。在查找過程中,ASL是所有查找過程中進行關鍵字比較次數的平均值。數學定義為:
其中,n是查找表的長度,P(i)是查找第i個數據元素的概率,一般認為每個元素的P(i)都相等,為1/n,C(i)是找到第i個數據元素所需進行比較的次數。
二:順序查找
1.順序查找介紹
順序查找又稱線性查找,主要用線性表進行查找,分為對一般的無序線性表的順序查找和對按關鍵字有序的順序表的順序查找。
2.一般線性表的順序查找
這是最簡單的查找方法,基本思想是從線性表的一端開始,逐個檢查關鍵字是否滿足給定的條件,查找成功返回在線性表中位置,查找失敗返回查找失敗的信息。
算法結構如下:
typedef struct{
ElemType *elem; //元素存儲空間的基址,建表時按實際長度分配,0號位留空放哨兵
int TableLen; //表的長度
}SSTable
int Search_Seq(SSTable ST,ElemType Key){
ST.elem[0] = key; //哨兵
for(i = ST.TableLen;ST.elem[i] != key;--i); //從后往前找
return i; //不存在關鍵字為key的元素,i為0時退出for循環
}
將ST.elem[0]稱為"哨兵",目的是使得算法里循環不必判斷數組是否越界。當滿足i == 0時可以直接跳出,避免不需要的判斷語句,提高程序效率。
平均查找長度分析:有n個元素的線性表,當定位到第i個元素時,需要進行n-i+1次關鍵字的比較(從后往前比較),則C(i)=n-i+1。查找成功時,當每個P(i)都相等,為1/n時,順序查找的平均查找長度為:
查找失敗時,與各關鍵字的比價次數是n+1次,則ASL(不成功)=n+1。
由上面的分析可以知道,順序表的優點是對數據存儲沒有要求,順序鏈表和鏈式鏈表皆可,對表中記錄的有序性也沒有要求。缺點是當n比較大時,效率比較慢,ASL比較大。
3.有序表的順序查找
有序表的順序查找成功查找時和一般順序表的查找一樣,但是查找失敗時,因為在查找之前就知道表是關鍵字有序的,則查找失敗時可以不用再比較表的另一端就能返回查找失敗的信息,提高效率,降低ASL。
這里只分析查找失敗的ASL,查找成功時的ASL和前面的一樣。查找失敗時,查找指針一定走到了某個失敗結點,是我們虛構的空結點,實際上並不存在。所以查找結點失敗時所查找的長度等於它上面的圓形結點所在的層數,則ASL為:
(1+2+3+4+..+n+n)÷(n+1) = n/2 + n/n+1
其中q(j)是到達第j個失敗結點的概率,相等查找概率的情形下為1/(n+1),l(j)是第j個失敗結點所在的層數。
三:折半查找
1.折半查找介紹
折半查找又稱二分查找,用二分法進行查找,僅適用於有序的順序表,不能用於鏈表。
2.折半查找基本思想
首先用要查找的關鍵字k與中間位置的結點的關鍵字相比較,這個中間結點把線性表分成了兩個子表,若比較結果相等則查找完成;若不相等,再根據k與該中間結點關鍵字的比較大小確定下一步查找哪個子表,這樣遞歸進行下去,直到找到滿足條件的結點或者該線性表中沒有這樣的結點。查找成功,返回查找的元素;查找不成功,返回查找失敗的信息。
折半查找有非遞歸和遞歸兩種方法。
3.折半查找非遞歸算法
int Binary_Search(SeqList L,ElemType key){
int low = 0,high = L.TableLen - 1,mid;
while(low <= high){
mid = (low + high) / 2
if(L.elem[mid] == key)
return mid;
else if(L.elem[mid] > key)
high = mid - 1;
else
low = mid + 1;
}
return -1;
}
4.折半查找遞歸算法
int Binary_Search(SeqList L,ElemType key,int low,int high){
mid = (low + high) / 2
if(low <= high){
if(L.elem[mid] == key)
return mid;
else if(L.elem[mid] > key)
return Binary_Search(L,key,low,mid - 1);
else
return Binary_Search(L,key,mid + 1,high);
}
return -1;
}
// di一次傳入的數據,這里*L指向順序表,也可以不加*,要具體而定
Binary_Search(*L,key,0,*L.TableLen - 1)
4.折半查找判定樹
折半查找可以用一個平衡二叉樹來描述,稱為判定樹,如下:
書中的圓形結點表示一個記錄,結點中的值為該記錄的關鍵字值;樹中最下面的葉節點都是方形的,表示查找不成功的情況。從中可以看到,查找成功的長度是從根節點到目的圓結點的路徑上的結點數,而查找不成功時的查找長度為從根節點到對應失敗結點的父結點的路徑上的結點數。這個二叉樹按中序遍歷元素逐步遞增。若序列有n個元素,則對應的判定樹有n個圓形的非葉節點和n+1個方形的葉結點。
5.折半查找的ASL
用折半查找查找到給定值的比較次數不會超過樹的高度,在等概率查找時,查找成功的平均查找長度為:
其中,h是樹高,並且元素個數為n時樹高h為 ,所以折半查找成功的時間復雜度為 ,一般情況下比順序查找的效率高。
查找失敗的時候,則計算失敗結點父結點的高度,然后ASL為:
每一層失敗結點的父結點高度*失敗節點個數之和與失敗結點總數之差。
6.折半查找的特性
折半查找需要線性表具有隨機存取的特性,則只適用於順序存儲結構,且要求元素按關鍵字有序排列。
四:分塊查找
1.介紹
分塊查找又稱索引順序查找,分塊查找是折半查找和順序查找的一種改進方法,折半查找雖然具有很好的性能,但其前提條件時線性表順序存儲而且按照關鍵碼排序,這一前提條件在結點樹很大且表元素動態變化時是難以滿足的。而順序查找可以解決表元素動態變化的要求,但查找效率很低。分塊查找吸收了順序查找和折半查找各自的優點,既有動態結構,又適合於快速查找。
2.基本思想
分塊查找要求把一個大的線性表分解成若干塊,每塊內的節點可以任意存放,但塊與塊之間必須排序。此外,還要建立一個索引表,把每塊內的最大關鍵碼值作為索引表的關鍵碼值,按塊的順序存放到一個輔助數組中。查找時,首先在索引表中進行查找,確定要找的節點所在的塊。由於索引表是排序的,因此,對索引表的查找可以采用順序查找或折半查找;然后,在相應的塊內采用順序查找,即可找到對應的節點。
3.分塊查找結構
4.分塊查找ASL
分塊查找的平均查找長度是索引查找和塊內查找平均長度之和。若一個查找表長度為n,均勻分為b塊,每塊有s個記錄,在等概率情況下,塊內和索引表中均采用順序查找,則ASL為:
ASL=(b+1)/2+(s+1)/2
此時,若s=√n,則平均查找長度取最小值√n+1 。
若對索引表進行折半查找時,則平均查找長度為:
ASL=(s+1)/2+log2(b+1)-1
這里要注意,log2(b+1)取最大值上限。