有序表查找

很久沒上博客園了,以前說好的一週寫一個博客來記錄本身的考研計劃也落空了。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上,歡迎查看。

有序表查找源碼

相關文章
相關標籤/搜索