插值查找-有序表查找_20230411

插值查找-有序表查找

  1. 前言

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

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

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

  1. 具体算法

算法的核心思想是对分割查找点的计算,《数据结构》严蔚敏版本给出的定义是,
s p l i t = ( ( k e y − s t . e l e m [ l ] . k e y ) ∗ ( h − l + 1 ) ) / ( s t . e l e m [ h ] . k e y − s t . e l e m [ l ] . k e y ) 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的查找点为:
s p l i t = f i n d _   i n t e r p o l a t i o n _ p o i n t ( s t , k e y , l o w , h i g h ) + l o w − 1 split = find \_\ interpolation\_point(st, key, low, high) + low-1 split=find_ interpolation_point(st,key,low,high)+low1
由于计算分割点的函数做了舍去处理,所以这里需要采用的计算方式:
s p l i t = f i n d _ i n t e r p o l a t i o n _ p o i n t ( s t , k e y , l o w , h i g h ) + l o w ; 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语言版)》-严蔚敏,清华大学出版社

你可能感兴趣的:(算法,数据结构,插值查找)