插值查找-有序表查找_20230411
Posted Jasonchen1224
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了插值查找-有序表查找_20230411相关的知识,希望对你有一定的参考价值。
插值查找-有序表查找
- 前言
有序表的查找一般分为二分查找(折半查找),斐波那契查找以及插值查找方法,前面我们讨论了斐波那契查找算法的具体实现,本文着手讨论插值查找算法。
插值查找只适用于关键字均匀分布的情况,在这种情况下,对于表厂较长的表,其查找的平均性能比折半查找要优异,如果关键字分布不均匀,其查找效率相对而言会很低,尤其是某个关键字很大,它对查找位置的影响就非常大,从而影响分割下标的移动速度。
插值查找也是利用分治的方法对表进行分割,与二分查找不同的地方在于,它不是对有序表进行均匀分割,而是按照关键字的大小进行比例分割。
- 具体算法
算法的核心思想是对分割查找点的计算,《数据结构》严蔚敏版本给出的定义是,
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=((key−st.elem[l].key)∗(h−l+1))/(st.elem[h].key−st.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));
- 其它主要算法的实现
利用迭代或者递归的方式实现插值查找,首先采用迭代的方式进行查找,
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)+low−1
由于计算分割点的函数做了舍去处理,所以这里需要采用的计算方式:
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;
- 算法分析
如果数值分布不均一,那么split的值移动的速度会非常慢,导致查询效率比二叉树低,算法时间复杂度过高。渎职可以分析有序数组arr[]=5,13,19,21,37,56,64,75,80,88,92,157,200,235,270在利用插值查找88的时候出现的情况。
- 小结
通过本文分析,理解了插值查找的应用场景以及局限性,另外对于插值点查询函数做了些许优化,在遇到相邻数值相等的有序表中,采用中值替代插值点。
参考资料:
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 代码实现