1. 程式人生 > 其它 >二分查詢、插值查詢、斐波那契查詢詳解 通俗易懂

二分查詢、插值查詢、斐波那契查詢詳解 通俗易懂

技術標籤:演算法資料結構演算法資料結構查詢二分查詢

二分查詢詳解

基本二分查詢有 3 種基本方式:

  • 折半查詢
  • 插值查詢
  • 斐波那契查詢

一、折半查詢

「折半查詢」這是最基本的二分查詢,類似一個小遊戲:

小明紙上寫上一個 100 以內數給小紅來猜,小明會告訴小紅結果是偏大還是偏小,問小紅幾次可以猜出

思路十分簡單,程式碼如下:


(1)遞迴式

/** 二分查詢遞迴 */
public static int recursionBinarySearch(int[] lookupTable, int target, int low, int high){
  if (lookupTable[low] >
target || lookupTable[high] < target){ return -1; } int middle = (low + high)/2; if (target < lookupTable[middle]){ return recursionBinarySearch(lookupTable,target,low,middle); }else if (target > lookupTable[middle]){ return recursionBinarySearch(lookupTable,target,middle,
high); } return middle; }

(2)非遞迴式:

/** 非遞迴式 */
public static int noRecursionBinarySearch(int[] lookupTable, int target){
    int low = 0;
    int high = lookupTable.length -1;
    int middle;
    if (lookupTable[low] > target || lookupTable[high] < target){
        return -1;
    }
    while
(low != high){ middle = (low + high)/2; if (target < lookupTable[middle]){ high = middle; }else if (target > lookupTable[middle]){ low = middle; }else { return middle; } } return -1; }

二、插值查詢

折半查詢有一個問題:

比如你要查詢英文詞典裡的「apple」這個單詞, 按照人類的思維,你應該從字典的前面開始找而不是從字典的中間開始找

「插入查詢」就是通過人類的思維對摺半查詢進行優化,讓查詢的位置儘量靠近目標位置,而不是盲目的二分查詢


在二分查詢中值指標 middle 的計算公式:

middle = (low + high)/2;

在「插入查詢」中值指標 middle 的計算公式:

middle = low + ((target - lookupTable[low]) * (height - low) / (lookupTable[height] - lookupTable[low]));

CodeCogsEqn (1)

程式碼在「折半查詢」中替換 middle 的表示式即可

public static int interpolationSerch(int[] lookupTable, int target, int low, int high){
  if (lookupTable[low] > target || lookupTable[high] < target){
    return -1;
  }
  // 替換 middle 表示式
  int middle = low + ((target - lookupTable[low]) * (height - low) / (lookupTable[height] - lookupTable[low]));
  if (target < lookupTable[middle]){
    return recursionBinarySearch(lookupTable,target,low,middle);
  }else if (target > lookupTable[middle]){
    return recursionBinarySearch(lookupTable,target,middle,high);
  }
  return middle;
}

三、斐波那契查詢

「斐波那契查詢」是基於「黃金分割」的「二分查詢」

3.1 黃金分割

黃金分割是指將整體一分為二,較大部分與整體部分的比值等於較小部分與較大部分的比值,其 比值 約為 0.618。這個比例被公認為是最能引起美感的 比例,因此被稱為黃金分割

3.2 基於黃金分割的「斐波那契數列」

基於黃金分割的的「斐波那契數列」從第三個數開始後面一個數等於相鄰前面 2 個數的和(即:a[i] = a[i-1] + a[i-2]

這樣的數列產生的效果都是 元素和前面一個元素的比值越來越接近黃金比例(a[i-1]/a[i] 的值越來越接近黃金比例)

比如:{1,1,2,3,5,8,13,21,34,55}

int[] fibonacciSequence = new int[]{1,1,2,3,5,8,13,21,34,55};

程式碼

public static int fibonacciSearch(int[] lookupTable,int[] f,int target){
    int low = 0;
    int high = lookupTable.length - 1;
    // k 是 Fibonacci 分割陣列下標
    int k = 0;
    int middle = 0;
    while (f[k] < high){
        k ++;
    }
    //利用 Java 工具類構造 f[k] 長度的查詢表,解決原有查詢表元素不夠的問題
    int[] temp = Arrays.copyOf(lookupTable,f[k]);
    while (low <= high){
        middle = low + f[k - 1];
        if (target < lookupTable[middle]){
            high = middle -1;
            k --;
        }else if (target > lookupTable[middle]){
            low = middle + 1;
            k -= 2;
        }else{
            return middle < high ? middle : high;
        }
    }
    return -1;
}

測試

int[] fibonacciSequence = new int[]{1,1,2,3,5,8,13,21,34,55};
int[] lookupTable = new int[] {0,1,2,3,4,5,6,7,8,9};
System.out.println(fibonacciSearch(lookupTable, fibonacciSequence, 3));

3.3 優點

  • 平均效能「斐波那契查詢」好於「折半查詢」
  • 「斐波那契查詢」計算 middle 的時候 使用加減法 而不是除法,會微弱提升效率

四、最後

3 種基本的二分查詢 折半查詢、插值查詢、斐波那契查詢 時間複雜度都是 Olog(n),但是 3 個各有優劣,實際開發中應根據實際的業務來使用。