查詢演算法之插值查詢
查詢演算法介紹:
1.線性查詢
2.二分查詢
3.插值查詢
4.斐波那契查詢(黃金分割數列)
一、插值查詢法的介紹
插值查詢原理介紹:
1.插值查詢演算法類似於二分查詢,不同的是插值查詢每次從自適應mid
處開始查詢。
2.將折半查詢中的求mid索引的公式進行優化
,key 代表查詢的值findValue
公式為:int midIndex = low + (high - low) * (key - arr[low]) / (arr[high]- arr[low]);
對應程式碼:int mid= left+(right-left) *(findvalue -arr[left])/ (arr[right]-arr[left])
二、演算法思路解析
二分查詢法為什麼是left + right / 2
呢?
因為二分法查詢的思想
:基於陣列的有序性
,每次都將當前的陣列分為兩半
,通過關鍵字
和中間元素的比較
,立即排除掉其中不可能存在和鍵值相等的元素的那一半。
所以使用通過left + right / 2
,並比較Key和arr[mid]大小
,丟掉"一半的元素"
(從 left + right / 2 可以看出被排除的一半元素不會納入到下一次的比較中了)
插值查詢為什麼是(findvalue-arr[left])/(arr[right]-arr[left])
?
首先我們根據基於陣列的有序性
,想一想查詢的位置一定要從中間開始查詢嗎?
打個比方:
我們在一本英文字典裡面查詢apple這個單詞
的時候, 你肯定不會從字典中間
開始查詢, 而是從字典開頭
部分開始翻,因為覺得這樣的找法才是比較快
的。
那麼為什麼你會從頭部分開始翻呢?因為知道字典是按照字母有序排序
的,並且apple這個單詞是a開頭,所以從頭開始翻。
那麼這時就有了一個思路
: 如果能在查詢前較準確地
預測關鍵字
在陣列中的位置
的話,這樣的查詢方法能比二分查詢提高更多的效能!
所以根據差值公式( key - arr[low]) / (arr[high] -arr[low])
,將要查詢的關鍵字 key 與查詢表中的最大、最小記錄的關鍵字比較的查詢方法。
三、通過應用示例認識插值查詢演算法
舉例:
有一陣列arr=[1,2,3,4,5,6,7,8......100]
假設我們需要查詢的值為:1
按照二分法查詢
:需要 mid = (left + right ) / 2
,折半…再折半…才找到1
按照插值查詢演算法
:int mid = left+(right-left) \* (findvalue -arr[left]) / (arr[right]-arr[left])
對應的程式碼:int mid= 0+(99-0) * (1- 1) / (100-1) = 0+99 * 0 / 99 = 0
直接找到值:1(下標為0)
比如我們查詢的值為:100(下標為99)
對應的程式碼:int mid=0 + (99-0) * (100-1) /(100-1)= 0+99 * 99/99 = 0+99=99
/**
* @param arr 陣列
* @param left 左邊索引
* @param right 右邊索引
* @param findValue 查詢值
* @return 如果找到則返回對應的下標,沒有找到返回-1 即可
*/
//編寫插值查詢方法
public static int insertValueSearch(int[] arr,int left,int right,int findValue){
//一、沒有找到的情況或者查詢的值(小於陣列最小值或者大於陣列最大值)
if(left>right||findValue<arr[0]||findValue>arr[arr.length-1]){
return -1;
}
//找出預測陣列中的位置
int mid=left+(right - left) * (findValue - arr[left]) / (arr[right]-arr[left]);
//對應的預測值
int midValue=arr[mid];
//若查詢的值比定位的值大,則需要向右遞迴
if(findValue>midValue){
//若`findVale>arr[mid]`,則說明查詢的數`findValue在右邊`,進行遞迴`向右查詢`
return insertValueSearch(arr,mid+1,right,findValue);
}else if(findValue<midValue){
//若`findVale<arr[mid]`,則說明查詢的數`findValue在左邊`,進行遞迴`向左查詢`
return insertValueSearch(arr,left,mid -1,findValue);
}else{
//若`findVale = =arr[mid]`,則說明`查詢的數已找到並返回下標`
return mid;
}
}
int[] arr = new int[100];
for (int i = 0; i < 100; i++) {
arr[i] = i + 1;
}
/**
* 在元素數值均勻分佈的有序數組裡面, 用這種方法查詢是很快的。
* 特別的,對絕對均勻分佈的陣列(相鄰元素差值相同), 插值查詢用一次比較就能查詢成功
* 當然了,前提是陣列中元素數值是均勻分佈的
* 如果是對 1,2,40,99,1000這種分佈很不均勻的陣列, 插值查詢的計算會起到反效果, 就不如二分查找了
*/
int index=insertValueSearch(arr,0,arr.length-1,100);
System.out.println("查詢的值對應下標是:"+index);
執行結果如下:
查詢的值對應下標是:99
四、插值查詢注意事項
在元素數值均勻分佈的有序陣列
裡面, 用這種方法查詢是很快
的。特別的,對絕對均勻分佈的陣列(相鄰元素差值相同), 插值查詢用一次比較就能查詢成功
。
當然了, 前提是陣列中元素數值是均勻分佈的
, 如果是對 1,2,40,99,1000 這種分佈很不均勻的陣列
, 插值查詢的計算會起到反效果
, 就不如二分查找了
五、演算法複雜度分析
時間複雜度平均為:O(logn)