很久沒上博客園了,以前說好的一週寫一個博客來記錄本身的考研計劃也落空了。git
忙着複習,很久都沒有打開電腦,計劃也都是寫在紙上了。最新開始數據結構的複習纔打開了電腦。github
開始敲代碼的感受真好。看來我註定是一個碼農了。之後仍是要多敲敲代碼,畢竟是之後吃飯的傢伙,三日不練,生疏啊。算法
不嘮叨了,說說今天要寫的主題——有序表查找。
(ps 這篇博客是查看程傑老師的大話數據結構後,參考網絡上的文章寫成的。優缺點和時間複雜度這段徹底抄錄的程傑老師的原話)。數組
1、定義網絡
就是字面上的意思,在一張有序表中進行查找。有序表是啥?就是數據按照必定順序排好的表,而不是一堆雜亂無章的數據。數據結構
有序表查找的基本前提就是數據是有序的。性能
2、幾種常見的有序表查找方法spa
1.折半查找3d
折半查找(Binary Search) 又稱爲 二分查找。code
折半查找的基本思想是:
將原始數據分爲等份的兩部分,比較關鍵字與中間值的大小,若是關鍵字小於中間值,說明關鍵字落在左半部分,將查找範圍縮小爲左半部分,繼續折半查找;
若是關鍵字大於中間值,說明關鍵字落在右半部分,將查找分爲縮小爲右半部分,繼續折半查找。
經過關鍵字與中間值的對比,不斷縮小查找範圍,最終查找數據。原理圖以下。
二分法的關鍵是中間值(也就是分隔)的選取。經過分隔,咱們將查找區間縮小,經過不斷縮小查找範圍,來查找數據。
二、插值查找
二分法將空間分隔的方法很是粗糙,就是講區間折半。
考慮這樣一組數據 {0, 1, 3, 4, 5, 7, 9, 10, 12, 13, 14} ,須要查找的數據是10。
觀察這個數組,數組的先後分佈存在相對均勻,若是咱們使用折半查找,咱們共須要查找 4 次,才能查找到數據。這就是不考慮數據分佈,粗糙地選取分隔的後果。
爲了改進折半查找的缺點,咱們從新選取分隔。
通過算法科學家的推到,改進方案以下:
分隔的取值變成上圖第二個公式。若是使用第二個公式,上述的查找只須要 1 次就能夠輕鬆查找到。
三、斐波那契查找
斐波那契查找是利用黃金分隔原理來實現的。它的本質和二分查找、插值查找沒有區別,都是經過設置分隔,不斷將區間縮小,最後查找到關鍵字的。
與以前兩個查找方法類似,斐波那契的不一樣也是分隔設置的不一樣,它是經過斐波那契數列來設置的。
斐波那契數列 F = {0, 1, 1, 2, 3, 5, 8, 13, 21, 34, ...}
斐波那契查找的一個限制就是,src的數據個數須要是斐波那契數列中的元素之一。
例如src = {0, 1, 16, 24, 35, 47, 59, 62, 73, 88, 99} 。
該數組的個數爲11個,11並非斐波那契數列的元素,13纔是,因此須要將數組擴容到13個,即令src[11]=src[12]=99。
斐波那契查找的具體代碼以下
/** * 從有序表中查找數據 * @param key 須要查找的數據 * @param src 有序表 * @return */ public static int fobSearch(int key,int[] src){ int length = src.length; int low = 0; //low high 的初始值分別等於有序表索引的最小值和最大值 int high = length-1; int fobInex = 0; // 咱們後面須要用到的 斐波那契數組中的索引值 int middle = 0 ; //二分法的分隔值 //使用斐波那契查找的要求 就是有序表的元素個數必須是斐波那契數組元素的值 while(length > getFobonacci(fobInex)){ fobInex ++; } //若是有序表的元素個數不等於斐波那契數組元素的值,則須要在後面補全 int newCapacity = getFobonacci(fobInex);//新數組的大小 if(length < newCapacity){ src = Arrays.copyOf(src, newCapacity); for(int i=length;i<newCapacity;i++){//將後續的數值補全 src[i] = src[i-1]; } } HelpUtils.printIntArray(src);//打印一下補全的數組 while(low <= high){ middle = low + getFobonacci(fobInex-1)-1; if (key < src[middle]) { //若是當前查找記錄小於當前分隔記錄 high = middle - 1; fobInex = fobInex - 1; } else if (key > src[middle]) { low = middle + 1; fobInex = fobInex - 2; } else{ if (middle < length) { return middle; } else{ return -1; } } } return -1; }
能夠看到分隔的選取依賴於斐波那契數列。
3、優缺點和時間複雜度
二分查找、插值查找和斐波那契查找的時間複雜度都是O(logn)。
二分查找的前提條件是須要有序表順序存儲,對於靜態查找表,一次排序後再也不變化,這樣的算法已經比較好了。
可是對於須要頻繁執行插入或者刪除操做的數據集來講,維護有序表的排序會帶來不小的工做量,並不適合使用。
插值查找,對於表長較大,而關鍵字分佈比較均勻的查找表來講,插值查找算法的平均性能要比折半查找好得多。
反之,若是數據中分佈相似{0,1,9999,999999}這樣極端不均勻的數據,用插值查找未必是最合適的選擇。
斐波那契查找,就平均性能而言,要優於二分查找,可是若是是最壞的狀況,好比key=0,那麼始終在左側長半區在查找,查找的效率要低於折半查找。
比較關鍵的一點是,插值、折半都須要進行比較複雜的乘除法運算,
而斐波那契只須要進行簡單的加減運算,在海量數據的查找過程當中,這種細微的差異可能會影響最終的查找效率。
上述三個有序表查找算法的具體代碼都放在個人github上,歡迎查看。