一、插分查找
在一个0~10000之间的100个元素从小到大均匀分布的数组中查找5,我们自然会考虑从数组下标较小的开始查找,因此二分查找还有提高空间。
mid = (low+high)/2 = low+1/2(high-low)。在这公式中我们将这个1/2进行修改:
mid = low + (key-a[low])/a[high]-a[low](high-low)。
比如在a[11] = {0,1,16,4,35,47,59,62,73,88,99},查找16,按照原来二分查找需要四次,改进后为:二次。其中mid= 1+ (16-1)/(99-1) *(10-1) = 2.377.
针对代码只需将:mid = (low+high)/2 改为 mid = low + (high-low)*(key-a[low])/(a[high]-a[low])即可。
int Search_Bin(SSTable ST, KeyType key) //插分查找
{ /* 在有序表ST中插分查找其关键字等于key的数据元素。若找到,则函数值为 */
/* 该元素在表中的位置,否则为0。 */
int low, high, mid;
low = 1; /* 置区间初值 */
high = ST.length;
while (low <= high)
{
mid = low + (high-low)*(key-a[low])/(a[high]-a[low]);
if EQ(key, ST.elem[mid].key) /* 找到待查元素 */
return mid;
else if LT(key, ST.elem[mid].key)
high = mid - 1; /* 继续在前半区间进行查找 */
else
low = mid + 1; /* 继续在后半区间进行查找 */
}
return 0; /* 顺序表中不存在待查元素 */
}
但是若加入数组分布不均匀,用插值查找未必是合适的选择。如{0,1,2,2000,2001….}。
二、斐波那契查找
斐波那契查找与折半查找很相似,他是根据斐波那契序列的特点对有序表进行分割的。他要求开始表中记录的个数为某个斐波那契数小1,及n=Fk-1;
1、算法思想
斐波那契查找的核心是:
1)当 key=a[mid] 时,查找成功;
2)当 key<a[mid] 时,新的查找范围是第 low 个到第 mid-1 个,此时范围个数为 F[k-1] - 1 个,即数组左边的长度,所以要在[low, F[k - 1] - 1] 范围内查找;
3)当 key>a[mid] 时,新的查找范围是第 mid+1 个到第 high 个,此时范围个数为 F[k-2] - 1 个,即数组右边的长度,所以要在[F[k - 1] + 1 , high] 范围内查找。
关于斐波那契查找, 如果要查找的记录在右侧,则左侧的数据都不用再判断了,不断反复进行下去,对处于当中的大部分数据,其工作效率要高一些。所以尽管斐波那契查找的时间复杂度也为O(logn),但就平均性能来说,斐波那契查找要优于折半查找。可惜如果是最坏的情况,比如这里key=1,那么始终都处于左侧在查找,则查找效率低于折半查找。
还有关键一点,折半查找是进行加法与除法运算的(mid=(low+high)/2),插值查找则进行更复杂的四则运算(mid = low + (high - low) * ((key - a[low]) / (a[high] - a[low]))),而斐波那契查找只进行最简单的加减法运算(mid = low + F[k-1] - 1),在海量数据的查找过程中,这种细微的差别可能会影响最终的效率。
三、分块查找
分块查找又称索引查找。它是一种介于顺序查找和二分查找之间的查找方法。在分块查找中,只要求索引表是有序的,对块内结点没有排序要求,因此特别适合结点动态变化的情况。
1、存储结构
分块查找是由“分块有序”的线性表和索引表两部分构成。
分块有序的线性表:假设要排序的表为 R[0...N-1],将表均匀分成 b 块,前 b-1 块中记录个数为 s=N/b,最后一块记录数小于等于 s;
每一块中的关键字不一定有序,但前一块中的最大关键字必须小于后一块中的最小关键字。
注:这是使用分块查找的前提条件。
如上将表均匀分成b块后,抽取各块中的最大关键字和起始位置构成一个索引表 IDX[0...b-1]。
由于表 R 是分块有序的,所以索引表是一个递增有序表。
所示为一个索引顺序表。其中包括三个块,第一个块的起始地址为 0,块内最大关键字为 25;第二个块的起始地址为 5,块内最大关键字为 58,最小关键字为28,大于25;第三个块的起始地址为10,块内最大关键字为 88。
2、算法
分块查找算法有两个处理步骤:
(1) 首先查找索引表
因为分块查找表是“分块有序”的,所以我们可以通过索引表来锁定关键字所在的区间。
又因为索引表是递增有序的,所以查找索引可以使用顺序查找或二分查找。
(2) 然后在已确定的块中进行顺序查找
因为块中不一定是有序的,所以只能使用顺序查找。