> 文章列表 > 插值查找-有序表查找_20230411

插值查找-有序表查找_20230411

插值查找-有序表查找_20230411

插值查找-有序表查找

  1. 前言

有序表的查找一般分为二分查找(折半查找),斐波那契查找以及插值查找方法,前面我们讨论了斐波那契查找算法的具体实现,本文着手讨论插值查找算法。

插值查找只适用于关键字均匀分布的情况,在这种情况下,对于表厂较长的表,其查找的平均性能比折半查找要优异,如果关键字分布不均匀,其查找效率相对而言会很低,尤其是某个关键字很大,它对查找位置的影响就非常大,从而影响分割下标的移动速度。

插值查找也是利用分治的方法对表进行分割,与二分查找不同的地方在于,它不是对有序表进行均匀分割,而是按照关键字的大小进行比例分割。

  1. 具体算法

算法的核心思想是对分割查找点的计算,《数据结构》严蔚敏版本给出的定义是,
split=((key−st.elem[l].key)∗(h−l+1))/(st.elem[h].key−st.elem[l].key)split=((key - st.elem[l].key) * (h - l + 1))/ (st.elem[h].key - st.elem[l].key) split=((keyst.elem[l].key)(hl+1))/(st.elem[h].keyst.elem[l].key)
相当于把最大值和最小值进行等分,然后按照待查询关键字与最小关键字之间的间距,确认查询点距离low位置的长度。

由于有序表中可能存在相邻两个相等的元素或者low==high的情况,在这种情形之下,就需要特别处理,否则分母为零,计算会出现异常。

定义当st.elem[h].key== st.elem[l].key相等的时候,split=(l+m)/2的值取中间即可。通过定义函数,具体查找点的位置算法,

int find_interpolation_point(SSTable st, int key, int l, int h)
{if(EQ(st.elem[l].key,st.elem[h].key)){return (l+h)/2;}else{return (((key - st.elem[l].key) * (h - l + 1))/ (st.elem[h].key - st.elem[l].key));}
}
  1. 其它主要算法的实现

利用迭代或者递归的方式实现插值查找,首先采用迭代的方式进行查找,

int interpolation_search_iteration(SSTable st, KeyType key)
{int low;int high;int split;low=1;high=st.len;while(low<=high){split = find_interpolation_point(st, key, low, high) + low;if (EQ(key, st.elem[split].key)){return split;}else if (LT(key, st.elem[split].key)){high = split - 1;}else{low = split + 1;}}return 0;
}

特别值得一提的细节是,正常情况下split的查找点为:
split=find_interpolation_point(st,key,low,high)+low−1split = find \\_\\ interpolation\\_point(st, key, low, high) + low-1 split=find_ interpolation_point(st,key,low,high)+low1
由于计算分割点的函数做了舍去处理,所以这里需要采用的计算方式:
split=find_interpolation_point(st,key,low,high)+low;split = find\\_interpolation\\_point(st, key, low, high) + low; split=find_interpolation_point(st,key,low,high)+low;
如果采用第一种方式,就会出现分割点小于最小值的情况,读者可以自行尝试。

我们也尝试采用递归的方式进行插值查找,其思路与迭代相同:


int interpolation_search_recursion(int low, int high, SSTable st, KeyType key)
{int split;int low_side;int high_side;if(low>high){return 0;}else{split = find_interpolation_point(st, key, low, high) + low;if (EQ(key, st.elem[split].key)){return split;}else if (LT(key, st.elem[split].key)){low_side = interpolation_search_recursion(low, split - 1, st, key);return low_side;}else{high_side = interpolation_search_recursion(split + 1, high, st, key);return high_side;}}}
  1. 算法分析

如果数值分布不均一,那么split的值移动的速度会非常慢,导致查询效率比二叉树低,算法时间复杂度过高。渎职可以分析有序数组arr[]={5,13,19,21,37,56,64,75,80,88,92,157,200,235,270}在利用插值查找88的时候出现的情况。

  1. 小结

通过本文分析,理解了插值查找的应用场景以及局限性,另外对于插值点查询函数做了些许优化,在遇到相邻数值相等的有序表中,采用中值替代插值点。

参考资料:

1.《数据结构(C语言版)》-严蔚敏,清华大学出版社