本文根據《大話數據結構》一書,實現了Java版的順序查找、折半查找、插值查找、斐波那契查找。
注:為與書一致,記錄均從下標為1開始。
順序表查找
順序查找
順序查找(Sequential Search):從第一個到最后一個記錄依次與給定值比較,若相等則查找成功。
順序查找優化:設置哨兵,可以避免每次循環都判斷是否越界。在數據量很多時能提高效率。
時間復雜度:O(n),n為記錄的數。
以下為順序查找算法及其優化的Java代碼:
package Sequential_Search; /** * 順序表查找 * 數組下標為0的位置不用來儲存實際內容 * @author Yongh * */ public class Sequential_Search { /* * 順序查找 */ public int seqSearch(int[] arr,int key) { int n=arr.length; for(int i=1;i<n;i++) { //i從1開始 if(key==arr[i]) return i; } return 0; } /* * 順序查找優化,帶哨兵 */ public int seqSearch2(int[] arr,int key) { int i=arr.length-1; arr[0]=key; //將arr[0]設為哨兵 while(arr[i]!=key) i--; return i; //返回0說明查找失敗 } public static void main(String[] args) { int[] arr = {0,45,68,32,15}; Sequential_Search aSearch = new Sequential_Search(); System.out.println(aSearch.seqSearch(arr, 15)); System.out.println(aSearch.seqSearch(arr, 45)); } }

4 1
有序表查找
折半查找(二分查找)
折半查找,又稱作二分查找。必須滿足兩個前提:
1.存儲結構必須是順序存儲
2.關鍵碼必須有序排列
假設數據按升序排列。從中間項與關鍵值(key)開始對比,若關鍵值(key)>中間值,則在右半區間繼續查找,反之則左半區間繼續查找。以此類推,直至找到匹配值,或者查找內無記錄,查找失敗。
時間復雜度:O(logn),可從二叉樹的性質4推得。
折半查找的Java實現代碼:
package OrderedTable_Search; /** * 折半查找 * @author Yongh * */ public class BinarySearch { public int binarySearch(int[] arr,int n,int key) { int low=1; int high=n; while(low<=high) { int mid = (low+high)/2; if(arr[mid]<key) low=mid+1; //要+1 else if(arr[mid]>key) high=mid-1; //要-1 else return mid; } return 0; } public static void main(String[] args) { int[] arr = {0,1,16,24,35,47,59,62,73,88,99}; int n=arr.length-1; int key=62; BinarySearch aSearch = new BinarySearch(); System.out.println(aSearch.binarySearch(arr, n, key)); } }

4
插值查找
對於表長較大,關鍵字分布比較均勻的查找表來說,可以采用插值查找:
將折半查找中代碼的第12行
改進為:
改進后的第12行代碼如下:
int mid = low + (high - low) * (key - arr[low]) / (arr[high] - arr[low]);/*插值*/
注意:關鍵字分布不均勻的情況下,該方法不一定比折半查找要好。
斐波那契查找
斐波那契數列如下所示:
斐波那契查找原理與前兩種相似,僅僅改變了中間結點(mid)的位置,mid不再是中間或插值得到,而是位於黃金分割點附近,即mid=low+F(k-1)-1(F代表斐波那契數列),如下圖所示:
對F(k-1)-1的理解:
由斐波那契數列 F[k]=F[k-1]+F[k-2] 的性質,可以得到 (F[k]-1)=(F[k-1]-1)+(F[k-2]-1)+1 。該式說明:只要順序表的長度為F[k]-1,則可以將該表分成長度為F[k-1]-1和F[k-2]-1的兩段,即如上圖所示。從而中間位置為mid=low+F(k-1)-1
類似的,每一子段也可以用相同的方式分割,從而方便編程。
但順序表長度n不一定剛好等於F[k]-1,所以需要將原來的順序表長度n增加至F[k]-1。這里的k值只要能使得F[k]-1恰好大於或等於n即可,由以下代碼得到:
while(n>fib(k)-1) k++;
順序表長度增加后,新增的位置(從n+1到F[k]-1位置),都賦為n位置的值即可。
時間復雜度:O(logn)
以下為具體的Java代碼,還有不理解的地方可看對應處的注釋:
package OrderedTable_Search; /** * 斐波那契查找 * 下標為0位置不存儲記錄 * 順便編寫了斐波那契數列的代碼 * @author Yongh * */ public class FibonacciSearch { /* * 斐波那契數列 * 采用遞歸 */ public static int fib(int n) { if(n==0) return 0; if(n==1) return 1; return fib(n-1)+fib(n-2); } /* * 斐波那契數列 * 不采用遞歸 */ public static int fib2(int n) { int a=0; int b=1; if(n==0) return a; if(n==1) return b; int c=0; for(int i=2;i<=n;i++) { c=a+b; a=b; b=c; } return c; } /* * 斐波那契查找 */ public static int fibSearch(int[] arr,int n,int key) { int low=1; //記錄從1開始 int high=n; //high不用等於fib(k)-1,效果相同 int mid; int k=0; while(n>fib(k)-1) //獲取k值 k++; int[] temp = new int[fib(k)]; //因為無法直接對原數組arr[]增加長度,所以定義一個新的數組 System.arraycopy(arr, 0, temp, 0, arr.length); //采用System.arraycopy()進行數組間的賦值 for(int i=n+1;i<=fib(k)-1;i++) //對數組中新增的位置進行賦值 temp[i]=temp[n]; while(low<=high) { mid=low+fib(k-1)-1; if(temp[mid]>key) { high=mid-1; k=k-1; //對應上圖中的左段,長度F[k-1]-1 }else if(temp[mid]<key) { low=mid+1; k=k-2; //對應上圖中的右端,長度F[k-2]-1 }else { if(mid<=n) return mid; else return n; //當mid位於新增的數組中時,返回n } } return 0; } public static void main(String[] args) { int[] arr = {0,1,16,24,35,47,59,62,73,88,99}; int n=10; int key=59; System.out.println(fibSearch(arr, n, key)); //輸出結果為:6 } }