【查找算法】折半查找法

简介: 【查找算法】折半查找法

本篇文章将介绍折半查找算法。

@[toc]

何为折半查找?

上一篇文章介绍了顺序查找算法,我们知道,虽然顺序查找算法适用性高,但效率太低,那么能不能在此基础上继续提高算法的效率呢?

这个时候,折半查找诞生了,它的原理是每次都将待查找的记录所在的区间缩小一半,比如:
在这里插入图片描述
若要在该序列中查找元素值4,折半查找是如何做到的呢?

它需要先设置两个游标,一个指向最左边,一个指向最右边:
在这里插入图片描述这两个游标所表示的范围即为查找区间,初始我们在下标为1到10的区间内查找,这个查找也是讲究方法的,不是一个一个地去遍历查找。

我们还需要借助一个游标,用它来表示区间的中间位置:
在这里插入图片描述
这个mid表示的就是区间的中间位置,计算方法为:(low + high) / 2

此时我们让待查找元素值与中间位置元素值比较,若相等,则查找成功,直接返回mid值;

若不相等,此时分为两种情况,待查找元素值比mid位置的元素值大或者比mid位置的元素值小。因为我们要查找的元素值为4,4 < 5,所以待查找的元素值肯定在mid位置的左边,此时我们缩小查找区间,让游标high指向mid的前一个位置:
在这里插入图片描述
此时要重新计算mid的值,(1 + 4) / 2 = 2,所以mid应该指向下标2的位置:
在这里插入图片描述
继续让待查找元素值与mid位置元素值比较,此时4 > 2,所以待查找元素值肯定在mid位置的右边,继续缩小查找区间,让游标low指向mid的后一个元素:
在这里插入图片描述
此时再次重新计算mid的值,(3 + 4) / 2 = 3,让mid指向下标为3的位置:
在这里插入图片描述
继续比较待查找元素值与mid位置的值,```4 > ,待查找元素值仍然在mid位置的右边,再次缩小区间,让low指向mid的后一个位置:
在这里插入图片描述

此时计算mid的值,(4 + 4) / 2
在这里插入图片描述
很巧的是三个游标指向了同一个位置,此时再比较,4 = 4,查找成功,返回mid的值为4。

我们再来查找一个序列中不存在的值,比如查找15,初始状态如下:
在这里插入图片描述
开始比较,15比mid位置的元素值大,说明待查找元素在mid位置的右边,缩小区间,让low指向mid位置的后一个元素,然后重新计算mid:
在这里插入图片描述
继续比较,15比mid位置的元素值大,继续缩小区间,重新计算mid:
在这里插入图片描述
继续比较,15比mid位置的元素值大,继续缩小区间,重新计算mid:
在这里插入图片描述
再次比较,15比mid位置的元素值大,此时再次缩小区间,游标low指向下标为11的位置,此时所有的区间均已查找完毕,所以查找的终止条件应为:low > high,若游标满足该条件,则证明查找失败。

算法实现

有了前面的分析铺垫,算法实现就会非常简单:

//折半查找算法
int BiSearch(SSTable st,int key){
   
   
    int low,high,mid;
    //为游标low、high设初始值
    low = 1;
    high = st.length;
    while(low <= high){
   
       //当low > high时退出循环
        //计算游标mid的位置
        mid = (low + high) / 2;
        if(st.elem[mid].key == key){
   
       
            //若当前位置元素值等于待查找元素值,查找成功,返回mid
            return mid;
        }else if(st.elem[mid].key < key){
   
   
            //若当前位置元素值小于待查找元素值,则表示待查找元素值在当前位置的右边
            low = mid + 1;        //修改游标low
        }else {
   
   
            //此时说明待查找元素值在当前位置的左边
            high = mid - 1;        //修改游标high
        }
    }
    return 0;    //查找失败,返回0
}

递归实现

从分析折半查找的过程之后,大家可能就有感觉,这个查找算法是能够用递归实现的, 因为每次的查找过程是一样的,下面是递归的折半查找算法实现:

//折半查找算法递归实现
int BiSearch(SSTable st,int key,int low,int high){
   
   
    int mid;
    if(low > high){
   
   
        //递归结束条件
        return 0;    //查找失败,返回0
    }
    //求出游标mid
    mid = (low + high) / 2;
    if(st.elem[mid].key == key){
   
   
        //查找成功,返回mid
        return mid;
    }else if(st.elem[mid].key < key){
   
   
        //此时待查找元素在mid位置的右边,递归调用
        BiSearch(st,key,mid + 1,high);    //修改游标low
    }else{
   
   
        //反之,修改游标high
        BiSearch(st,key,low,mid - 1);
    }

}

非常简单,不做过多解释。

效率分析

还是来分析一下折半查找算法的效率。

比如下面的一个序列:
在这里插入图片描述
我们知道,折半查找第一次去比较的是中间位置的元素,在该序列中,中间位置值为5,如果我们要查找的元素值就为5,那么只需比较一次即可;

若待查找元素值为3,则需要比较两次才能找到;

折半查找算法的效率同样受待查找元素值影响。

若我们把只需比较一次即可找到的元素5作为根结点形成一棵二叉树:
在这里插入图片描述
其中各个结点所在的层数即为比较此时,比如结点2在二叉树中的第二层,则需比较2次才能查找到元素2;结点7在第四层,则需比较4次才能查找到元素7。

所以每个元素值的比较次数都小于等于二叉树的深度,而根据二叉树性质可得出结论:
$$比较次数 ≤「log_2n」 + 1$$

由此还可以得出查找不成功的比较次数等于路径上的内部结点数,即:
$$比较次数 ≤「log_2n」 + 1$$

假定每个元素的查找概率相等,则查找成功时的平均长度为:
$$\frac{n + 1}{n}log_2(n + 1) - 1 ≈ log_2(n + 1) - 1$$

所以折半查找算法的时间复杂度为:logn。

总结一下,折半查找法的优点是:

效率比顺序查找高

但也有缺点:

只适用于顺序表,且限于顺序存储结构(对线性链表无效)
只适用于有序的序列

相关文章
|
算法 Java
折半查找算法[二分查找法]算法的实现和解决整数溢出问题~
折半查找算法[二分查找法]算法的实现和解决整数溢出问题~
|
存储 算法 数据库
经典算法学习之-----顺序查找,折半查找,索引查找(二)
经典算法学习之-----顺序查找,折半查找,索引查找(二)
223 0
|
1月前
|
算法 Java 索引
数据结构与算法学习十五:常用查找算法介绍,线性排序、二分查找(折半查找)算法、差值查找算法、斐波那契(黄金分割法)查找算法
四种常用的查找算法:顺序查找、二分查找(折半查找)、插值查找和斐波那契查找,并提供了Java语言的实现代码和测试结果。
20 0
|
算法 C语言
C语言:使用 普通方法 和 二分查找算法(折半查找算法) 在一个有序数组中查找具体的某个数字n-2
第一步: (1). 设置初始数组:int arr[]。 (2). 生成相关变量: int n = 0; -- 存放从键盘输入的要查找的值; int i = 0; -- 循环变量;
|
算法 Java 索引
经典算法学习之-----顺序查找,折半查找,索引查找(三)
经典算法学习之-----顺序查找,折半查找,索引查找(三)
46 0
|
6月前
|
算法
【408数据结构与算法】—折半插入排序(十六)
【408数据结构与算法】—折半插入排序(十六)
|
算法 C语言
C语言-折半查找(二分查找)算法详解
C语言-折半查找(二分查找)算法详解
172 0
|
搜索推荐 算法
深入浅出排序算法之直接插入排序(拓展:折半插入排序)
深入浅出排序算法之直接插入排序(拓展:折半插入排序)