【二分查找】:高效搜索算法的原理、实现与避坑指南

        搜索算法是解决问题的基础工具之一,而在众多搜索算法中,二分查找(Binary Search)凭借其惊人的效率脱颖而出——它能在对数时间复杂度(O(log n))内定位目标,尤其适用于有序数据集的高效检索。从数据库索引到编程框架的底层实现,二分查找的身影无处不在。

        然而,这一看似简单的算法却暗藏玄机:边界的处理、循环终止条件的设定、重复元素的特殊场景……稍有不慎便会陷入“死循环”或“漏查”的陷阱。本文将深入剖析二分查找的核心思想,文末对应 leetcode 习题助你彻底掌握这一“高效搜索利器”!

目录

一、算法简介

二、算法执行过程

三、算法原理

        1.初始化指针

        2.循环查找

        3.比较目标值与中间元素:

        4.中止条件

四、完整实现代码

五、关键点解析

六、性能对比

八、LeetCode练习题


一、算法简介

        二分查找(Binary Search)是一种在有序数组中快速定位目标元素的高效搜索算法,其核心思想是每次将搜索范围缩小一半

二、算法执行过程

基于分治思想(Divide and Conquer),算法通过三个关键步骤实现高速搜索:

  1. 区间划分:确定当前搜索区间的中间位置

  2. 比较决策:将中间元素与目标值进行对比

  3. 范围收缩:根据比较结果排除不可能的半边区间

如同查字典时快速翻到目标字母区域的过程,二分查找每次操作都能排除约一半的无效数据。

三、算法原理

        1.初始化指针

        设置左指针 left = 0 和 右指针 right = size - 1

        说明:数组下标从0开始,所以在计算出 数组元素个数 的基础上 right 再 -1,定义搜索区间为闭区间 [ left , right ]

        2.循环查找

        计算中间索引:

        1.传统写法:

        适用于规模较小(数组元素较少)时:int mid = ( left + rigth ) / 2

        2.改进写法:

        适用于规模较大(数组元素较多)时:int mid = left + (right - left) / 2

        当数组非常大时,这时 left + right 可能会超过 int 的最大值,为了避免(left+right) 可能的整数溢出(整形只能储存四个字节数据),采用这种写法

        3.比较目标值 key 与中间元素:

        key < arr[mid]:要查找的值不在 mid 右边,调整右边界 right = mid-1

        key > arr[mid]:要查找的值不在 mid 左边,调整左边界 left = mid+1

        key = arr[mid]:说明找到了,直接返回 mid

        4.中止条件

        当 left > right 时,数组查找完毕,元素不存在,返回 -1

四、完整实现代码

#include <stdio.h>

int binary_search(int* arr, int key, int size) 
{
    //初始化双指针
    int left = 0;
    int right = size - 1;
    
    while (left <= right) // 有效的搜索区间
    {
        //计算中间位置
        int mid=(left+rigth)/2; // 适用于规模较小时
        //int mid = left + (right - left)/2; // 规模较大时,避免整数溢出
        
        if (key < arr[mid]) 
        {
            right = mid - 1;// 收缩右边界
        } 
        else if (key > arr[mid]) 
        {
            left = mid + 1;// 收缩左边界
        } 
        else 
        {
            return mid;
        }
    }
    return -1; // 未找到返回-1
}

int main() 
{
    int arr[] = {1,2,3,4,5,6,7,8,9,10};
    int key = 7;
    int len = sizeof(arr)/sizeof(arr[0]);
    
    int index = binary_search(arr, key, len);
    printf("元素 %d 的索引位置是:%d\n", key, index);
    return 0;
}

五、关键点解析

  1. 前置条件数组必须有序排列(升序或降序)

  2. 边界处理:闭区间查找需包含 left == right 的情况

  3. 中间值计算 ( 预防溢出 ) :推荐 left + (right - left) / 2

  4. 未找到情况处理:循环结束后返回 -1

  5. 循环条件:使用使用 while ( left <= right ) 确保最后一次比较的有效性

  6. 指针移动

    • 找到中间元素后立即排除已检查区域

    • 每次循环至少排除一半元素

  7. 时间复杂度

最优情况平均情况最坏情况

O(1)

(首次mid即命中)

O(log n)O(log n)

        注:每次迭代都将问题规模n减半,经过log₂n次操作后必然终止。

               其时间复杂度为O(log n),相比线性查找的O(n)具有显著性能优势,比线性查找快指数级

      8 . 空间复杂度:

             仅需常数级别的额外空间存储指针变量,空间复杂度为O(1)。

六、性能对比

测试数据规模线性查找二分查找
100万元素500ms<1ms
10亿元素不可行30次循环内完成

八、LeetCode练习题

        二分查找通过不断缩小搜索区间实现高效查找,核心在于正确处理边界条件和中间值计算。掌握该算法不仅能够提升编码能力,更是培养分治思维的重要实践。建议结合LeetCode题目进行强化训练,加深对算法细节的理解。

  1. 704. 二分查找(基础实现)

  2. 35. 搜索插入位置(边界处理)

  3. 34. 在排序数组中查找元素的第一个和最后一个位置(变种应用)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值