本文根據《大話數據結構》一書,實現了Java版的順序查找、折半查找、插值查找、斐波那契查找。html
注:爲與書一致,記錄均從下標爲1開始。java
順序查找(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
折半查找,又稱做二分查找。必須知足兩個前提:ide
1.存儲結構必須是順序存儲
2.關鍵碼必須有序排列優化
假設數據按升序排列。從中間項與關鍵值(key)開始對比,若關鍵值(key)>中間值,則在右半區間繼續查找,反之則左半區間繼續查找。以此類推,直至找到匹配值,或者查找內無記錄,查找失敗。spa
時間複雜度:O(logn),可從二叉樹的性質4推得。code
折半查找的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 } }