二分查找
二分查找又称折半查找,是有序查找算法的一种,即查找的序列必须是递增或递减的。它的原理类似于猜1至100中的任意数字,比如我们猜数字27,如果我们按顺序猜的话需要27次,如果随机猜的话,次数无法保证,可能很少也可能很多,我们按照这种方式猜,我们先猜中间数100/2=50,发现大了,我们再猜1至50的中间数50/2=25,发现小了,再猜25至50中间数(25+50)/2=37(取整),照此规律猜7次便可猜中27,使用这种方法猜1至100中任意数字不会超过7次。下面我们按照这种方法查找{1,3,20,24,44,67,71,82,90,110} 序列(length=10)中的数字71,
首先我们先定义三个游标分别指向头(top)尾(end)和中间值(mid)。第一次我们将top=0,end=9(length-1),mid=[(0+9)/2]=4。
mid=4指向44小于71,我们取序列中大于44的部分继续查找。我们将top=mid+1=5,end不变。
取mid=[top+end]/2=7,mid=7指向82大于71,我们令end=mid-1,top不变。
取mid=[top+end]/2=5,mid=5指向67小于71,令top=mid+1,end不变。
取mid=[top+end]/2=6。mid=6指向71,查找成功,返回。
综上所述编程逻辑如下:
- 初始化top=0,end=length-1;
- 取mid=[top+end]/2,若mid指向查找值,成功返回;若mid指向值大于查找值则指向步骤3,否则指向步骤4;
- 令end=mid-1,执行步骤2;
- 令top=mid+1,执行步骤2;
代码如下:
int binary_search(int *a, int n, int key)
{
int top, end, mid;
top = 0;
end = n - 1;
while (top <= end)
{
mid = (top + end) / 2;
if (key < a[mid])
end = mid - 1;
else if (key > a[mid])
top = mid + 1;
else
return mid;
}
return 0;
}
函数返回所查找值下标。
插值查找
在二分查找中,mid指向中间数,然而在一些情况下这种取法并不好,比如在取值范围0~10000之间100个元素从小到大均匀分布的数组中查找5,如果从中间数开始查找,则中间数必定远远大于5,这样我们会遍历很多不必要的元素,所以我们考虑从下标较小的元素开始查找。
二分查找的mid计算如下:
m
i
d
=
t
o
p
+
e
n
d
2
=
t
o
p
+
1
2
(
e
n
d
−
t
o
p
)
mid=\frac{top+end}{2}=top+\frac{1}{2}(end-top)
mid=2top+end=top+21(end−top)
现在我们将公式改进一下:
m
i
d
=
t
o
p
+
k
e
y
−
a
[
t
o
p
]
a
[
e
n
d
]
−
a
[
t
o
p
]
(
e
n
d
−
t
o
p
)
mid=top+\frac{key-a[top]}{a[end]-a[top]}(end-top)
mid=top+a[end]−a[top]key−a[top](end−top)
改进之后性能有什么改善吗?假设我们查找{1,16,24,35,47,59,62,73,88,99}。如果我们查找16,使用二分查找需要4次,但如果使用插值查找,mid=0+(16-1)/(99-1)
×
\times
×(9-0)=1.377,取整mid=1,直接便可查找到16这个元素。
插值查找代码如下:
int insert_search(int *a, int n, int key)
{
int top, end, mid;
top = 0;
end = n - 1;
while (top <= end)
{
mid = top + (end - top) * (key - a[top]) / (a[end] - a[top]);
if (key < a[mid])
end = mid - 1;
else if (key > a[mid])
top = mid + 1;
else
return mid;
}
return 0;
}
斐波那契查找
很抱歉,斐波那契查找我虽然会但限于领悟程度无法很好的讲解,我找到一个将斐波那契查找很好的文章大家可以参考
性能分析
由上述分析可知,二分查找最多只需查找一半的数据,它的时间复杂度为O(logn),好于顺序查找的O(n),但二分查找需要数据有序,对于静态查找而言比较好,但对于需要频繁插入删除操作的数据而言,维护其数据有序需要额外开销得不偿失;插值查找时间复杂度也为O(logn),但对于表较长且关键字分布比较均匀的查找表来说,插值查找比二分查找更好;而斐波那契查找则在海量查找表中发挥优势。