1. 程式人生 > >查詢演算法(一)順序表查詢

查詢演算法(一)順序表查詢

順序表查詢分為:

  • 順序查詢

  • 有序查詢

    • 折半查詢
    • 插值查詢
    • 斐波那契查詢
  • 線性索引查詢
    • 稠密索引查詢
    • 分塊索引查詢
    • 倒序查詢

1.順序查詢

基本思想:順序查詢是最基本的查詢技術,查詢過程是:從表中的第一個或者最後一個元素開始,依次將元素和待查詢的值進行比較,若相同,則查詢成功;若直到表尾或表頭,元素和待查詢的值都不相同,則查詢不成功

程式碼實現:

public class SequentialSearch {

    public static void main(String[] args) {
        int[] a = {0,1,16,24,35,47,59,62
,73,88,99}; System.out.println(sequential(a,62)); } public static int sequential(int[] a, int target){ int len = a.length; for(int i=0;i<len;i++){ if(a[i]==target){ return i; } } return -1; } }

複雜度分析:
查詢成功的平均查詢時間為(1+2+3+…+n)/n = (n+1)/2;
查詢失敗需要進行n+1次比較;
所以順序查詢的時間複雜度為O(n)

2.有序查詢

2.1折半查詢

折半查詢又叫二分查詢,其前提是序列為有序的,若為無需序列,需要先進行排序。

基本思想: 因為序列為有序序列,首先將待查詢的資料k與序列中間元素a[mid]進行比較,如相同,則查詢成功;若k > a[mid],則表明其存在於後半部分,否則在前半部分,由此繼續遞迴查詢,直到查詢到或者查詢失敗。

程式碼實現:

import java.util.Arrays;

public class BinarySearch {

    public static void main(String[] args) {
        int[] a = {0
,1,16,24,35,47,59,62,73,88,99}; System.out.println(binary(a,62)); } public static int binary(int[] a, int target){ int len = a.length; int i = 0; int j = len-1; int mid; //首先對資料進行排序 Arrays.sort(a); while(i<=j){ mid = (i+j)/2; if(a[mid] == target) return mid; if(target > a[mid]){ i = mid+1; }else{ j = mid - 1; } } return -1; } }

時間複雜度分析: 每次比較可以排除掉一半的資料,所以時間複雜度為O(logn)。

注意:二分查詢的前提是元素是有序的,所以對於靜態表,一次排序後不再變化,其查詢效率是不錯的,但是對於需要頻繁插入和刪除的資料來說,單純維護其序列有序就會帶來不小的開銷,所以在這種情況下,二分查詢並不適用。

2.2 插值查詢

插值查詢是根據待查詢的資料key和序列中最大最小值比較後的查詢方法,是二分查詢的一種變形。

基本思想: 對於折半查詢來說,每次都從1/2處進行,但是例如對於1~10000中間的100數裡查詢數值5來說,首先需要考慮從陣列下標較小的開始查詢,而不是直接從1/2處,這樣才可以提供查詢的效率。
所以將查詢點的計算改為:(key - a[low]) / (a[high] - a[low]);
所以 mid = low + (key - a[low]) * (high - low) / (a[high] - a[low])

程式碼實現:

只需要將二分查詢中的mid = (i+j)/2 改為
mid = i + (j-i)*(target - a[i])/(a[j]-a[i]);

時間複雜度分析: 其平均時間複雜度O(logn),對於表較長,且數值均勻分佈的序列來說,插值查詢平均效能要比折半查詢好得多,但是對於極端不均勻分佈的資料,差值查詢不一定是是合適的選擇。

2.3斐波那契查詢

斐波那契查詢也是二分查詢的一種變形,也是一種有序查詢演算法。
首先介紹下黃金分割:是指飾物各部分之間存在的一定數學比例關係,即將整體一分為二,較大部分/較小部分 = 整體 / 較大部分,其比值約為1.618:1。
對有斐波那契數列,後面每一個數是前面兩個數字的和,並且可以發現,隨著斐波那契數列的遞增,前後兩個數字的比值越來約接近0.618,在斐波那契查詢中,便是利用到這一特性。

基本思想: 將黃金分割的概念運用在查詢點的計算上,提高查詢效率。
如下圖所示(圖片來源https://blog.csdn.net/sayhello_world/article/details/77200009,侵權刪),序列的長度為斐波那契數列中的一個數值減1,即n = F(k) - 1; 那麼插入點mid = low + (F(k-1) - 1)。
若 key == a[mid], 查詢成功;
若 key > a[mid], 則在右半部分,所以有left = mid+1,且 k = k-2;
若 key < a[mid], 則在左半部分,所以有right = mid-1,且 k = k-1;
這裡寫圖片描述

程式碼實現:

import java.util.Arrays;

public class FibonacciSearch {
    public static void main(String[] args) {
        int[] a = {0,1,16,24,35,47,59,62,73,88,99};
        System.out.println(Fibonacci(a,62));

    }

    public static int Fibonacci(int[] a, int target){
        int len = a.length;
        int[] F = creatFibonacci();

        int k = 0;
        //遍歷斐波那契數列,找到k的值
        for(int i = 0;i<F.length;i++){
            if(len > F[k]-1){
                k++;
            }
        }

        //當F[k]-1>n的是否,需要不全待查詢的序列,Arrays類中的copyOf方法將a陣列補長到F[k]-1
        int[] temp = Arrays.copyOf(a, F[k]-1);
        for(int i = len;i<F[k]-1;i++){
            temp[i] = temp[len-1];
        }

        int left = 0;
        int right = F[k]-1;
        int mid;

        while(left<right){
            mid = F[k-1] - 1;
            if(temp[mid] == target)
                return mid;
            if(target > temp[mid]){
                left = mid+1;
                k -= 2;
            }else{
                right = mid - 1;
                k -= 1;
            }
        }

        return -1;
    }

    //初始化一個斐波那契陣列,長度為20,F[19] = 6765,已經挺大的了,所以這裡定義的長度20
    public static int[] creatFibonacci(){
        int[] F = new int[20];

        F[0] = 0;
        F[1] = 1;

        for(int i = 2;i<20;i++){
            F[i] = F[i-1] + F[i-2];
        }
        return F;
    }
}

複雜度分析: 時間複雜度為O(logn)

總結:順序查詢不需要序列為有序的;而折半查詢、插值查詢以及斐波那契查詢則均是在序列有序的基礎上進行的,只是查詢點上有不同,各有各的優勢,在實際使用當中,需要綜合考慮來做出選擇。

3.線性索引查詢

在現實中,資料是不斷處於動態增長且無序的,前面有序查詢演算法需要基於有序的基礎上,所以對於大量增長非常快的資料集來說,維護資料有序的代價是非常非常大的。所以出現了索引。
索引就是把一個關鍵字與它對應的記錄相關聯的過程,加快查詢速度。
線性索引查詢就是將索引項集合組織為線性結構,稱為索引表。

3.1稠密索引

稠密索引是指在現象索引中,將資料集中的每個記錄對應一個索引項。
這裡寫圖片描述
索引項按照關鍵碼有序排列,當查詢關鍵字時,可以通過折半、插值、斐波那契等有序演算法進行查詢,大大提高效率。
對於稠密所以,意味著索引和資料集具有同樣的長度,當資料非常大時,稠密索引的查詢效率大大降低。

3.2分塊索引

為減少索引個數,將資料集進行分塊,對每塊建立索引項。
這些塊需要滿足一下兩個條件:

  • 快內無序,即對每一塊內的記錄不要求有序,維護快內有序需要付出大量的時間和空間代價;
  • 塊間有序,快間有序,才能在查詢中帶來效率。

這裡寫圖片描述

分塊索引查詢步驟:
(1)在分塊索引表中查詢關鍵字所在塊,因為塊間有序,所以同樣可以使用有序查詢演算法提高效率;
(2)根據塊首指標找到相應的塊,因為塊中無序,所以在塊中順序查詢的得到關鍵碼。

分塊查詢的時間複雜度為:O(√n)

3.3倒序索引

倒序索引的典型應用就是搜尋引擎。
倒序索引是建立被搜尋的關鍵詞和含這些關鍵字的頁面之間的對映,其中倒序指的是從關鍵詞中查詢到對應的源,而不是從源中查詢到相應的關鍵詞。
將倒序索引表排序後,同樣可以通過有序查詢演算法快速查詢到關鍵字,進而查詢到包含關鍵字的頁。
例如:為了檢索關鍵詞java,首先從倒序索引表中,找到該關鍵詞,然後便可以查詢到java所在的頁了。