查詢演算法 - 插值查詢
阿新 • • 發佈:2021-01-11
簡介
插值查詢,有序表的一種查詢方式。插值查詢是根據查詢關鍵字與查詢表中最大最小記錄關鍵字比較後的查詢方法。插值查詢基於二分查詢,將查詢點的選擇改進為自適應選擇,提高查詢效率。
時間複雜度
O(loglogN)
思路分析
插值查詢演算法類似於二分查詢,不同的是插值查詢每次從自適應mid處開始查詢。
將二分查詢中的求mid 索引的公式優化改進如圖
low 表示左邊索引left
high表示右邊索引right
key 表示 findVal
其原理是根據公式巧妙的算出比例值,恰好陣列是有序的,在關鍵字分佈均勻的情況下,這個比例值正好是key的位置。而關鍵字分佈不均勻時,算出的結果則是儘可能接近要匹配的元素的位置。
圖中公式換算成程式碼:
int mid = low + (high - low) * (key - arr[low]) / (arr[high] - arr[low])
等同於
int mid = left + (right - left) * (findVal - arr[left]) / (arr[right] - arr[left])
程式碼實現
public class InsertValueSearch {
public static void main(String[] args) {
int[] arr = {3, 9, 11, 89, 89, 89, 1200 , 1500};
List<Integer> findIndex = insertValueSearch(arr, 0, arr.length - 1, 89);
System.out.println(findIndex);
}
/**
* 插值查詢
*
* @param arr 有序陣列
* @param left 左索引
* @param right 右索引
* @param findVal 需要查詢的值
* @return 查詢到指定元素的索引
*/
public static List<Integer> insertValueSearch(int[] arr, int left, int right, int findVal) {
// left > right 如果左索引大於右索引 證明比較到剩下一個元素的下一次比較 也就是沒找到匹配的值
// findVal > arr[right] || findVal < arr[left] 比最大的值大 比最小的值小 證明肯定沒有匹配的元素
if (left > right || findVal > arr[right] || findVal < arr[left]) {
return new ArrayList<>();
}
// 根據關鍵字分佈情況 得到儘可能靠近需要匹配元素的附近位置
int mid = left + (right - left) * (findVal - arr[left]) / (arr[right] - arr[left]);
if (arr[mid] > findVal) {
// 當要查詢的元素比中值小 則需要往中值左邊找
return insertValueSearch(arr, left, mid - 1, findVal);
} else if (arr[mid] < findVal) {
// 當要查詢的元素比中值大 則需要往中值右邊找
return insertValueSearch(arr, mid + 1, right, findVal);
} else {
// 匹配到查詢的值
List<Integer> findIndex = new ArrayList<Integer>();
findIndex.add(mid);
int temp = mid;
// 尋找左邊是否有相同的值
while (true) {
if (--temp < 0 || arr[temp] != arr[mid]) {
break;
}
findIndex.add(temp);
}
temp = mid;
// x尋找右邊是否有相同的值
while (true) {
if (++temp > arr.length - 1 || arr[temp] != arr[mid]) {
break;
}
findIndex.add(temp);
}
return findIndex;
}
}
}
執行輸出:
[3, 4, 5]
總結
對於資料量較大,關鍵字分佈比較均勻的查詢表來說,採用插值查詢, 速度較快。
關鍵字分佈不均勻的情況下,該方法不一定比折半查詢要好。