1. 程式人生 > 其它 >查詢演算法之插值查詢

查詢演算法之插值查詢

技術標籤:查詢演算法演算法

查詢演算法介紹:

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)