查找算法(二)插值查找
二分查找(折半查找)
它的前提是线性表中的记录必须是有序的,线性表必须采用顺序存储。折半查找的基本思想是:在有序表中,取中间记录作为比较对象,若给定值与中间记录的关键字相等,则查找成功;若给定值小于中间记录的关键字,则在中间记录的左半区继续查找;若给定值大于中间记录的关键字,则在中间记录的右半区继续查找。不断重复上述过程,直到查找成功,或所有查找区域无记录,查找失败为止。
代码实现
int BinarySearch(int a[], int value){
int n = a.length;
int low = 0;
int high = n-1;
while(low <= high){
int mid = low + (high - low)/2;
if(a[mid] == value){
return mid;
}
elseif(a[mid] > value){
high = mid -1;
}
else{
low = mid + 1;
}
}
return -1;
}
递归版本
int BinarySearch(int a[], int value, int low, int high){
int mid = low + (high - low)/2;
if(a[mid] == value){
return mid;
}
elseif(a[mid] > value){
return BinarySearch(a,value,low,mid - 1);
}else{
return BinarySearch(a,value,mid + 1,high);
}
}
时间复杂度:O(log(n))
注:折半查找的前提条件是需要有序顺序存储,对于静态查找表,一次排序后不再变化,折半查找能得到不错的效率。但对于需要频繁执行插入或删除操作的数据集来说,维护有序的排序会带来不小的工作量,那就不建议使用了。
插值查找
基本思想:基于二分查找,将查找点的选择改进为自适应选择,可以提高查找效率。 将二分查找的插值计算公式改为
时间复杂度:平均情况O(loglog(n)),最坏O(log(n))
注:对于表长较大,而关键字分布又比较均匀的查找表来说,插值查找的平均性能比折半查找要好。
斐波那契查找
原理:斐波那契查找与折半查找很相似,他是根据斐波那契数列的特点对有序表进行分割的。他要求开始表中记录的个数比某个斐波那契数小1,即n=F(K)-1;当记录不满足时,后面的元素都赋值为最后一个值。(注:使得n=F(K)-1是因为:如果表中的数据为F(K)-1个,mid分割又用掉一个,剩下F(K)-2个,正好分给两个子序列,每个子序列的长度为F(K-1)-1和F(K-2)-1,格式和之前的统一,方便递归编程实现)
将value值与第mid位置的记录进行比较,比较结果分为三种:
- 相等,则返回mid
- 大于,low = mid + 1low=mid+1,k = k - 2k=k−2
- 小于,high = mid - 1high=mid−1,k = k - 1k=k−1
(说明:low=mid+1说明待查找的元素在[mid+1,high]范围内,k-=2 说明范围[mid+1,high]内的元素个数为F(k-2)-1个)
代码实现
int FibonacciSearch(int[] a, int value){
int[] F = {0,1,1,2,3,5,8,13,21,34,55};
int n, low, high, mid, k;
n = a.length;
low = 1;
high = n;
k = 0;//斐波那契数列的下标
while(n > F[k] - 1){//计算n位于斐波那契数列的位置
++k;
}
//省略将a后移一位,并且长度扩展到F(k)
for(int i = n + 1; i <= F[k] - 1; i++){//将不满的数值补全,长度为F(k) - 1
a[i] = a[n];
}
while(low <= high){
mid = low + F[k - 1] - 1;
if(value > a[mid]){
low = mid + 1;
k = k - 2;//mid右边的个数,即F[k-2]个
}elseif(value < a[mid]){
high = mid - 1;
k = k - 1;//mid左边的个数
}else{
if(mid < n){
return mid;
}else{
return n;
}
}
}
return -1;
}
总结: 平均性能平均性能:斐波那契>折半>插值
因为折半查找进行加法与除法运算(mid = (low + high) / 2),插值查找进行复杂的四则运算( mid = low + (key - a[low] / (a[high] - a[low]) * (high - low)) ),而斐波那契查找只是运用简单加减法运算 (mid = low + f[k-1] -1) ,在海量的数据查找过程中,这种席位的差别会影响最终的查找效率。三种有序表的查找本质上是分割点的选择不同,各有优劣,实际开发可根据数据的特点综合考虑再做决定。