插值查詢的簡單理解

程式設計師翔仔發表於2022-07-05

詳細描述

二分查詢是通過折半的方法,每一次都將搜尋範圍縮小至原來的二分之一,如果這個折半能夠實現到折四分之一甚至更多,效率將會更高。

插值查詢就是這樣的演算法,類似於二分查詢,插值查詢每次會從自適應處開始查詢,實質上是將 \(\frac1 2\) 處位置的查詢公式做了修改:

\[mid = \frac{low + high}{2} = low + \frac{1}{2}(high - low) \Rightarrow mid = low + \frac{key - a[low]}{a[high] - a[low]}(high - low) \]

插值查詢詳細的執行步驟如下:

  1. 在有序表中,通過比例公式取對應記錄作為比較物件;
  2. 若給定值與對應記錄的關鍵字相等,則查詢成功;
  3. 若給定值小於對應記錄的關鍵字,則在對應記錄的左半區繼續查詢;
  4. 若給定值大於對應記錄的關鍵字,則在中間記錄的右半區繼續查詢;
  5. 不斷重複上述過程,直到查詢成功,或所有查詢區域無記錄,查詢失敗為止。

問題解疑

插值查詢為什麼是 \(\frac{key - a[low]}{a[high] - a[low]}\)?

打個比方,在一本英文字典中查詢 apple 這個單詞的時候,肯定不會從字典中間開始查詢,而是從字典開頭部分開始翻,因為會覺得這樣的找法才是比較快的。

對於一個有序的序列,如果能在查詢前較準確的預測關鍵字在序列中的位置時,這樣的查詢方法能比二分查詢擁有更好的效能。

其中的差值公式 \(\frac{key - a[low]}{a[high] - a[low]}\) 是要將查詢的關鍵字與序列中的最大、最小記錄的關鍵字比較,獲取一個相對更準確的位置。

使用插值查詢有哪些注意事項?

對於均勻分佈的序列,插值查詢的效率是非常快。特別是對於絕對均勻分佈的序列(相鄰元素差值相同),插值查詢可以只做一次比較就查詢成功。

對於分佈很不均勻的序列,插值查詢的計算則會起到反效果,這時候反而不如二分查詢。

程式碼實現

查詢介面

package cn.fatedeity.algorithm.search;

public interface Search {
    int search(int[] numbers, int target);
}

插值查詢類

package cn.fatedeity.algorithm.search;

/**
 * 插值查詢類
 */
public class InterpolationSearch implements Search {
    private int search(int[] numbers, int target, int left, int right) {
        if (left > right) {
            return -1;
        } else if (left == right) {
            if (numbers[left] == target) {
                return left;
            } else {
                return -1;
            }
        }
        if (target < numbers[left] || target > numbers[right]) {
            return -1;
        }

        int scale = (target - numbers[left]) / (numbers[right] - numbers[left]);
        int mid = left + (int) Math.floor(scale * (right - left));
        if (numbers[mid] > target) {
            return this.search(numbers, target, left, mid - 1);
        } else if (numbers[mid] < target) {
            return this.search(numbers, target, mid + 1, right);
        } else {
            return mid;
        }
    }

    @Override
    public int search(int[] numbers, int target) {
        return this.search(numbers, target, 0, numbers.length - 1);
    }
}

相關文章