插值查找的簡單理解
詳細描述
二分查找是通過折半的方法,每一次都將搜索範圍縮小至原來的二分之一,如果這個折半能夠實現到折四分之一甚至更多,效率將會更高。
插值查找就是這樣的算法,類似於二分查找,插值查找每次會從自適應處開始查找,實質上是將 \(\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)
\]
\]
插值查找詳細的執行步驟如下:
- 在有序表中,通過比例公式取對應記錄作為比較對象;
- 若給定值與對應記錄的關鍵字相等,則查找成功;
- 若給定值小於對應記錄的關鍵字,則在對應記錄的左半區繼續查找;
- 若給定值大於對應記錄的關鍵字,則在中間記錄的右半區繼續查找;
- 不斷重複上述過程,直到查找成功,或所有查找區域無記錄,查找失敗為止。
問題解疑
插值查找為什麼是 \(\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);
}
}