參考書籍:數據結構(C語言版)嚴蔚敏吳偉民編著清華大學出版社
本文中的代碼可從這裏下載:https://github.com/qingyujean/data-structure
1.靜態查找表
查找的定義:給定一個值k,在含有n個結點的表(或文件)中找出關鍵字等於給定值k的結點,若找到,則查找成功,輸出該結點在表中的位置;否則查找失敗,輸出查找失敗的信息。
查找表是由具有同一類型(屬性)的數據元素(記錄)組成的集合。分靜態查找表和動態查找表兩類。
靜態查找表:僅對查找表進行查找操作,而不改變查找表中的數據元素;
動態查找表:對查找表除進行查找操作外,可能還要進行向表中插入數據元素,或刪除表中數據元素的表。
1.1.順序查找
查找過程:對給定的一關鍵字 K,從線性表(順序表或線性鏈表均可以)的一端開始,逐個進行記錄的關鍵字和 K 的比較,直到找到關鍵字等於 K 的記錄或到達表的另一端。通常在線性表的0號單元存儲key值以作爲“哨兵”。
監視哨作用:避免每步都去判斷是否查找結束。
測試用例:查找75和80
代碼實現:
package search;
public class StaticSearch {
/**
* @param args
*/
//在順序表ST中順序查找其關鍵字等於key的數據元素,若找到,則函數值爲該元素在表中的位置,否則返回0
public static int searchSeq(int[] st, int key){//st表的0號單元不使用
//從後往前找,並在0號單元設置"監視哨"
int i = st.length - 1;
st[0] = key;//設置監視哨
while(st[i] != key){
i--;
}
return i;
}
public static void main(String[] args) {
// TODO Auto-generated method stub
//順序查找測試
int st[] = {0, 10, 20, 40, 80, 30, 60, 25};//0號單元不使用
int key = 75;
int result = searchSeq(st, key);
if(result > 0)
System.out.println("查找" + key + "成功,在查找表中的位置是" + result);
else
System.out.println("查找" + key + "失敗");
key = 80;
result = searchSeq(st, key);
if(result > 0)
System.out.println("查找" + key + "成功,在查找表中的位置是" + result);
else
System.out.println("查找" + key + "失敗");
}
}
運行結果:
算法分析:
查找成功時的平均查找次數爲:
ASL=(1+2+3+4+……+n)/n = (n+1)/2 ①
查找不成功時的比較次數爲:
ASL=(n(n+1))/n = n+1 ②
則順序查找的平均查找長度爲:
ASL==(① + ②)/2 = 3(n+1)/4
優點:算法簡單,無需排序,採用順序和鏈式存儲均可。
缺點:平均查找長度較大。
1.2折半查找(有序表的查找)
查找思想:先確定待查找記錄所在的範圍,然後逐步縮小範圍,直到找到或確認找不到該記錄爲止。
前提條件:必須在具有順序存儲結構的有序表中進行。
示例:假設給定有序表中10個關鍵字爲8,17,25,44,68,77,98,100,115,125,將查找K=17和K=120的情況
代碼實現:
package search;
public class StaticSearch {
/**
* @param args
*/
//在有序表st中折半查找其關鍵字等於key的數據元素,若找到,則函數值爲該元素在表中的位置,否則返回0
public int searchBin(int[] st, int key){//st表的0號單元不使用
int low = 1, high = st.length - 1, mid = (low + high)/2;
while(low <= high){
if(st[mid] == key)
return mid;
else if(st[mid] > key)
high = mid - 1;
else
//st[mid] < key
low = mid + 1;
}
return 0;//low > high時表明查找失敗
}
public static void main(String[] args) {
// TODO Auto-generated method stub
//有序表的二分查找測試
int st[] = {0, 8, 17, 25, 44, 68, 77, 98, 100, 115, 125};//0號單元不使用
int key = 17;
int result = searchSeq(st, key);
if(result > 0)
System.out.println("查找" + key + "成功,在查找表中的位置是" + result);
else
System.out.println("查找" + key + "失敗");
key = 120;
result = searchSeq(st, key);
if(result > 0)
System.out.println("查找" + key + "成功,在查找表中的位置是" + result);
else
System.out.println("查找" + key + "失敗");
}
}
運行結果:
算法分析:
爲了分析二分查找的性能,可以用二叉樹來描述二分查找過程。把當前查找區間的中點作爲根結點,左子區間和右子區間分別作爲根的左子樹和右子樹,左子區間和右子區間再按類似的方法,由此得到的二叉樹稱爲二分查找的判定樹。
例:關鍵字序列{8,17,25,34,48,57,68}的判定樹
由於第 k 層結點的查找次數各爲 k 次(根結點爲第1層),而第 k 層結點數最多爲 2^k-1 個。假設該二叉樹深度爲 h,則等概率下,二分查找的成功的 ASL 爲:
最壞情形下:① 取等號;② 最大的結點數n = 2^h -1;
則h=log2(n+1),因此 ,ASL=(n+1)/n log2(n+1)-1
當 n 很大時,ASL --> log2(n+1)-1 ,則其時間複雜度爲O(log2n)。
二分查找的效率比順序查找高,但只能適用於有序表,而排序本身是一種很費時的運算,即使採用高效率的排序方法也要花費O(nlog2n)的時間;另外,二分查找只適用於順序存儲結構(對線性表表示無法進行二分查找)
1.3索引順序查找(分塊查找)
查找思想:是順序查找的一種改進方法,就是把被查找的表分成若干塊,每塊中記錄的存放順序是無序的,但塊與塊之間必須按關鍵字有序。即第一塊中任一記錄的關鍵字都小於第二塊中任一記錄的關鍵字,而第二塊中任一記錄的關鍵字都小於第三塊中任一記錄的關鍵字,依此類推。
該方法要爲被查找的表建立一個索引表,索引表中的一項對應於表中的一塊,索引表中含有這一塊中的最大關鍵字和指向塊內第一個記錄位置的指針,索引表中各項關鍵字有序。
查找步驟:
1)折半查找索引表(當然也可以順序查找索引表,整體的查找速度也是比原始的順序查找高的,但遠不及折半查找),確定要找的記錄所在塊;
2)在相應的塊中順序查找。
示例:
代碼實現:
package search;
public class StaticSearch {
/**
* @param args
*/
public static int searchIndex(int[][] index, int[] st, int key){//爲方便,index[],st[]均使用0號單元
//折半查找索引表
int low = 0, high = index[0].length - 1;
int block = index[0].length, elemsInBlock = st.length / block;
int start, mid = 0;
while(low < high){
mid = (low + high)/2;
if(index[0][mid] == key){
start = mid;
break;
}else if(index[0][mid] > key)
high = mid;
else
//index[0][mid] < key
low = mid + 1;
}
start = index[1][low];
//從start位置開始順序查找
int i = 0;
while(i < elemsInBlock && st[start+i] != key){
i++;
}
if(i == elemsInBlock)
return -1;
else
return start+i;
}
public static void main(String[] args) {
// TODO Auto-generated method stub
//索引順序查找,又叫分塊查找,是順序查找的一種改進方法,塊間可使用折半查找,塊內只能是順序查找
int[][] index = {{20, 53, 89},
{0, 5, 10}
};//索引表
int[] st = {18, 12, 8, 5, 20, 51, 36, 22, 29, 53, 89, 60, 72, 66, 76};//查找表
int key = 22;
int result = searchIndex(index, st, key);
if(result > -1)
System.out.println("查找" + key + "成功,在查找表中的位置是" + result);
else
System.out.println("查找" + key + "失敗");
key = 6;
result = searchIndex(index, st, key);
if(result > -1)
System.out.println("查找" + key + "成功,在查找表中的位置是" + result);
else
System.out.println("查找" + key + "失敗");
}
}
算法分析:
設把長度爲n的表分成均等的b個子表,每個子表有S個對象,則 b=n/s 。又設表中每個對象的查找概率相等,子表爲1/b,子表內各對象爲1/s 。
則順序查找確定塊的成功ASL爲:ASL1=(b+1)/2+(s+1)/2=(b+s)/2+1
折半查找確定塊的成功ASL爲: ASL2=log2(b+1)-1+(s+1)/2=log2(1+n/s)+s/2
可見,索引查找的ASL與表的長度n和子表內對象的個數s有關。