插值查找-有序表查找_20230411

Posted Jasonchen1224

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了插值查找-有序表查找_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语言版)》-严蔚敏,清华大学出版社

顺序表查找和有序表查找

查找里面顺比表查找和有序表查找(包括二分查找,插值查找,斐波那契查找)比较简单,直接贴代码,代码里面有详细注释。

  1 #include <iostream>
  2 using namespace std;
  3 
  4 //顺序表查找(线性查找、静态表查找)  时间复杂度为O(n)
  5 int Seq_Search(int *s,int n,int key)
  6 {
  7     s[0] = key;   //设置了一个哨兵,避免了每次比较一次过后都要判断查找位置是否越界
  8     int i = n;
  9     while (s[i] != key)
 10     {
 11         --i;
 12     }
 13     return i;
 14 }
 15 
 16 //二分查找(折半查找) 前提条件:记录的关键码必须有序,必须是顺序存储   时间复杂度为O(logn)
 17 int Binary_Search(int *s,int n,int key)
 18 {
 19     int low,mid,high;
 20     low = 1;
 21     high = n;
 22     while (low <= high)
 23     {
 24         mid = (low + high)/2;
 25         if (s[mid] > key)
 26         {
 27             high = mid - 1;
 28         }else if (s[mid] < key)
 29         {
 30             low = mid + 1;
 31         }else
 32             return mid;
 33     }
 34     return 0;
 35 }
 36 
 37 //插值查找(对于表长较大,关键字分布比较均匀的查找表来说,插值查找算法的平均性能要比二分查找算法的性能高)
 38 int B_Search(int *s,int n,int key)
 39 {
 40     int low,mid,high;
 41     low = 1;
 42     high = n;
 43     while (low <= high)
 44     {
 45         mid = low + (high - low)*(key - s[low])/(s[high] - s[low]);
 46         if (s[mid] > key)
 47         {
 48             high = mid - 1;
 49         }else if (s[mid] < key)
 50         {
 51             low = mid + 1;
 52         }else
 53             return mid;
 54     }
 55     return 0;
 56 }
 57 
 58 //计算斐波那契数列
 59 void Fibonacci(int *F)
 60 {
 61     F[0]=0;
 62     F[1]=1;
 63     for(int i =2;i<10;i++)  
 64     { 
 65         F[i] = F[i-1] + F[i-2];  
 66     } 
 67 }
 68 
 69 //斐波那契查找(时间复杂度是O(logn))
 70 int Fibonacci_Search(int *s,int n,int key,int *F)
 71 {
 72     int low,high,mid,i,k;
 73     low = 1;
 74     high = n;
 75     k = 0;  //k为斐波那契数列的下标
 76     while (n > F[k] - 1)  //计算n位于斐波那契数列的位置
 77     {
 78         k++;
 79     }
 80     for (i = n;i < F[k] -1;i++)  //将不满的数值补全
 81     {
 82         s[i] = s[n];
 83     }
 84     //开始查找
 85     while (low <= high)
 86     {
 87         mid = low + F[k] - 1;
 88         if (key < s[mid])
 89         {
 90             high = mid - 1;
 91             k = k - 1;   //斐波那契数列下标减一位
 92         }else if (key > s[mid])
 93         {
 94             low = mid + 1;
 95             k = k - 2;   //斐波那契数列下标减两位
 96         }else
 97         {
 98             if (mid <= n)
 99             {
100                 return mid;
101             }else
102                 return n;   //若mid>n,说明是补全的数值,返回n
103         }
104     }
105     return 0;
106 }
107 
108 void main()
109 {
110     int s[] = {0,1,16,24,35,47,59,62,73,88,99};
111     int n = 11;
112     int m,a;
113     int Fbi[10];
114     Fibonacci(Fbi);
115 
116     cout << "输入顺序表查找要查找的数字: ";
117     cin >> m;
118     a = Seq_Search(s,n,m);
119     cout << "顺序表查找" << m <<"的位置在:"<< a << endl << endl;
120 
121     cout << "输入二分查找要查找的数字: ";
122     cin >> m;
123     a = Binary_Search(s,n,m);
124     cout << "二分查找" << m <<"的位置在:"<< a << endl << endl;
125 
126     cout << "输入插值查找要查找的数字: ";
127     cin >> m;
128     a = B_Search(s,n,m);
129     cout << "插值查找" << m <<"的位置在:"<< a << endl << endl;
130 
131     cout << "输入斐波那契查找要查找的数字: ";
132     cin >> m;
133     a = Fibonacci_Search(s,n,m,Fbi);
134     cout << "斐波那契查找" << m <<"的位置在:"<< a << endl << endl;
135 
136     system("pause");
137 }

运行结果:

技术分享

以上是关于插值查找-有序表查找_20230411的主要内容,如果未能解决你的问题,请参考以下文章

[Algorithm]二分插值斐波那契查找算法 Java 代码实现

[Algorithm]二分插值斐波那契查找算法 Java 代码实现

数据结构查找---有序表查找(三种查找方式:折半,插值,斐波拉契查找)

算法插值查找算法

Python数据结构与算法(20)---插值查找

Python数据结构与算法(20)---插值查找