查找算法之折半查找法

一、基本概念

        折半查找(Binary Search),又称二分查找,查找效率高,但仅适用于顺序存储的有序表。其基本的思路如下:在一个有序表中,每次取中间记录数同目标值进行比较。若该记录数等于目标值,则查找成功;若大于目标值,则说明目标值在该查找表的左半区,此时我们往左半区进行查找;若小于目标值,则说明目标值在该查找表的右半区此时我们往右半区进行查找。如此往复,直至查找成功;若无和目标值相等的记录数,则查找失败。

        注意,上述描述是基于升序查找表基础上进行分析的。

二、实现思路

        我们首先需要定义三个指针Low ,  Mid ,  High ,分别指向查找区间的头、中、尾。

        我们再定义一个有序的顺序表,如下:

         我们设查找的目标值target = 21,基本流程如下:

         1、Low = 1,指向12;High = 12,指向23;Mid =\lfloor (Low + High)  / 2\rfloor = 6 ,指向17。此时Mid指向17 < 21,我们需要将Low指针右移至Mid + 1 = 7,往右半区查找。


        2、Low = 7 ,指向18;High = 12,指向23;Mid = \lfloor (Low + High) / 2\rfloor  = 9,指向20。此时Mid指向20 < 21,我们需要再次将Low指针右移至Mid + 1 = 10,往右半区查找。


        3、Low = 10,指向21;High = 12,指向23;Mid = \lfloor (Low + High) / 2\rfloor  = 11,指向22。此时Mid指向22 > 21,我们需要将High指针左移至Mid - 1 = 10,往左半区查找。


        4、Low = High = 10,指向21;Mid =\lfloor (Low + High) / 2\rfloor  = 10,指向21。此时Mid指向21 = 21,查找成功,目标值的位序就是Mid的值。


三、代码实现(C语言)

```

// 折半查找

int BitSearch(int *Arr , int Len , int Target)

{

    int Low , High , Mid;

    Low = 0;

    High = Len;

    while(Low <= High)

    {

        Mid = (Low + High) / 2;                // 折半

        if(Arr[Mid] == Target)                   // 查找成功

        {

            return Mid + 1;                        // 我们要返回的是位序

        }

        else if(Arr[Mid] > Target)            // 中间值大于目标值

        {

            High = Mid - 1;

        }

        else                                            // 中间值小于目标值

        {

            Low = Mid + 1;

        }

    }

    return  -1;                                       // 查找失败 

}

int main()

{

    int Array[12];

    // 生成有序数组

    for(int i = 0 ; i <= 11 ; i++)

    {

        Array[i] = i + 12;

    }

    printf("查找表:");

    for(int i = 0 ; i < 12 ; i++)

    {

        printf("%d\t" , Array[i]);

    }

    // 查找数值21的位序

    int target = 21;

    int Pos = BitSearch(Array , sizeof(Array) / sizeof(Array[0]) - 1 , target);

    printf("\n");

    if(target != -1)

    {

        printf("查找成功,数值%d的位序是:%d" , target , Pos);

    }

    else

    {

        printf("查找失败");

    }

    return 0;

}

```

四、效率分析

在数据结构中,我们都学过树这种数据结构。我们都知道,在一棵有n(n >= 0)个结点的完全二叉树中,树的深度k = \lfloor \log_2 n \rfloor + 1。折半查找的查找过程,也可以理解为一棵二叉排序树的构造过程。话不多说,直接上图

        1、第一轮查找,Low = 1 ,High = 12,Mid =\lfloor (Low + High) / 2\rfloor  = 6,指向17。此时17 < 21,我们需要将Low指针右移至Mid + 1 = 7,往右半区查找。此时查找的状态如下:


        2、第二轮查找,Low = 7,High = 12,Mid =\lfloor  (Low + High) / 2\rfloor  = 9,指向20。此时20 < 21,我们需要继续将Low指针右移至Mid + 1 = 10,往右半区继续查找。此时查找状态如下:


        3、第三轮查找,Low = 10,High = 12,Mid = \lfloor (Low + High) / 2\rfloor  = 11,指向22。此时22 > 21,我们需要将High指针左移至Mid - 1 = 10,往左半区查找。此时查找状态如下:


        4、第四轮查找,Low = 10,High = 10,Mid = \lfloor (Low + High) / 2\rfloor = 10,指向21.此时21 == 21,查找成功!此时的查找状态如下:


        我们可以看到,在这棵二叉树中,查找成功的最坏情况是查找次数等于该树的深度k。我们都知道,一棵二叉树的最大深度为\lfloor \log_2 n \rfloor  + 1。所以在查找成功的情况下,折半查找的最坏时间复杂度为\lfloor \log_2 n \rfloor + 1。那么,其平均查找长度必然小于等于\lfloor\log_2 n  \rfloor  + 1。在这棵树中,折半查找的平均查找长度如下:

        ASL(成功) = (1 * 2 + 2 * 4 + 3 * 5 ) / 12 =  25 / 12

        当查找失败的时,查找位置会落在二叉树的空链域中。一棵有n个结点的二叉树有2n个空链域,所以查找失败的平均查找长度如下;

        ASL(失败) = (3 * 3 + 4 * 10)  / 13 = 49 / 13

        完整二叉树如下(有那么一丝丝难看):


最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容