二分查詢、插值查詢、斐波那契查詢詳解 通俗易懂
阿新 • • 發佈:2021-01-04
二分查詢詳解
基本二分查詢有 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]));
程式碼在「折半查詢」中替換 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 個各有優劣,實際開發中應根據實際的業務來使用。