目录
解题思路
首先想要了解插值查找需要了解折半查找,而他们又都共属于减治法。
减治法
就是将一个大的问题,分解成若干个小的问题,然后解决其中的一个小的问题,在建立起其之间的联系,以求得最后的答案。
折半查找
由于本文主要是讲述插值算法,对折半算法就进行一个概述:折半查找,又叫二分法查找,就是将搜索区域每次减小一半的查找,以此来降低搜索难度
插值查找
插值查找是改进的折半查找,将原有的50%改成了按特定比例的查找。
我们来举一个例子:
假设一个字典是均匀分布的(a,b,c,d每个字母开头的单词的页数都一样),我们需要找到D这个字母开头的词。我们是不会从中间进行查找,我们会从大概4/26的附近位置。这就是插值查找的主要实现思路。
以在升序序列中查找目标元素为例,插值查找算法的实现思路是: ·
1.初始状态下,将整个序列作为搜索区域(假设为 [Begin, End]);
2.找到搜索区域内的插值元素(假设所在位置为 M),和目标元素进行比对。如果相等,则搜索成功;如果插值元素大于目标元素,表明目标元素位于插值元素的左侧,将 [Begin, M-1] 作为新的搜素区域;反之,若插值元素小于目标元素,表明目标元素位于插值元素的右侧,将 [M+1, End] 作为新的搜素区域;
3.重复执行第二步,直至找到目标元素。如果搜索区域无法再缩小,且区域内不包含任何元素,表明整个序列中没有目标元素,查找失败。
MId的计算
那么这个M的值是如何计算出来的呢?
在折半查找中: Mid = (Begin+End)/2 =Begin+1/2(End-begin)
Mid = Begin + ( (X - A[Begin]) / (A[End] - A[Begin]) ) *(End - Begin)
Mid:计算得出的元素的位置;
End:搜索区域内最后一个元素所在的位置;
Begin:搜索区域内第一个元素所在的位置;
X:要查找的目标元素
A[]:表示整个待搜索序列。
对比得出公式是将1/2改为了( (X - A[Begin]) / (A[End] - A[Begin]) )
(X - A[Begin])是得到了插值元素和搜索区域内第一个元素的差值(类比D在字母表中为4-0)
(A[End] - A[Begin]) 得到了整个搜索区域的差值(类比于字母表中有26个字母26-0)
例1
1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
在这样一个数组中我们需要找到2
带入公式Mid = Begin + ( (X - A[Begin]) / (A[End] - A[Begin]) ) *(End - Begin)
Mid = 0 + ( ( 2 - 1 ) / ( 10 - 1 ) ) * ( 9 - 0 ) = 1
而得到的一正是目标元素2的值,在这个例子中明显插值查找的效率比折半查找的高得多
缺点
例2
1 | 2 | 3 | 4 | 5 | 18 | 19 | 21 | 77 | 322 | 888 | 1025 | 1527 | 2229 |
Mid = Begin + ( (X - A[Begin]) / (A[End] - A[Begin]) ) * (End - Begin)
Mid = 0 + ( ( 77 - 1 ) / ( 2229 - 1 ) ) *( 13 - 0 ) = 0.44--1
Mid = 1 + ( ( 77 - 2 ) / ( 2229 - 2 ) ) *( 13 - 1 ) = 1.40--2 ……………
看得出每次搜索区域只减少了1,很明显在此数组中插值查找远不如折半查找的效率高。
从而我们得到:当有序序列中的元素呈现均匀分布时,插值查找算法的查找效率要优于折半查找算法;反之,如果有序序列不满足均匀分布的特征,插值查找算法的查找效率不如折半查找算法。 所谓均匀分布,是指序列中各个相邻元素的差值近似相等。例如,{10, 20, 30, 40, 50} 就是一个均匀分布的升序序列,各个相邻元素的差值为 10。再比如 {100, 500, 2000, 5000} 是一个升序序列,但各相邻元素之间的差值相差巨大,不具备均匀分布的特征。
总的来说,有序+均匀分布是插值查找的最大缺点
具体实现
int interpolation_search(int* arr, int begin, int end, int x)
{
int mid = 0;
if (begin > end) { return -1; }
if (begin == end) {
if (x == arr[begin]) { return begin; }
return -1;
}
mid = begin + ((x - arr[begin]) / (arr[end] - arr[begin]) * (end - begin));
if (x == arr[mid]) { return mid; }
if (x < arr[mid]) { return interpolation_search(arr, begin, mid - 1, x); }
else { return interpolation_search(arr, mid + 1, end, x); }
}
插值算法的时间复杂度:
第一次假设搜索区域折为:N/m1
则第二次搜索区域折为:N/(m1*m2)
第三次:N/(m1*m2*m3)
…………
假设当第n次搜索区域只剩下一个元素,即数列已被搜索完成即:N/(m1*m2*m3*…*mn)=1
那我们得到n的值为以m1*m2*m3*...mn为底N的对数
则O(n)=log2N
资料来源
https://blog.csdn.net/huanghanqian/article/details/79088171
https://www.bilibili.com/video/BV1nY4y1N7v1/?spm_id_from=333.337.search-card.all.click&vd_source=05aa685c08d081347bed6210640e9f07 https://blog.csdn.net/qq_29542611/article/details/79392661
http://c.biancheng.net/algorithm/interpolation-search.html