当前位置: 首页 > article >正文

深入解析二分查找算法:原理、实现与变种

目录

一、核心思想

二、前提条件

三、标准二分查找实现

场景:在有序数组中查找某个值是否存在。

关键点:

四、变种问题与实现

1. 查找第一个等于目标的位置(Lower Bound)

2. 查找最后一个等于目标的位置(Upper Bound)

3. 查找插入位置

五、STL中的二分查找

六、常见问题与陷阱

七、应用场景

八、总结


一、核心思想

二分查找(Binary Search)是一种在有序数组中快速查找目标值的算法。其核心思想是:

  1. 分治策略:通过不断将搜索区间缩小一半来逼近目标值。

  2. 时间复杂度:O(log n),效率远高于线性查找的 O(n)。


二、前提条件
  • 数组必须有序(升序或降序)。

  • 若数组无序,需先排序(时间复杂度 O(n log n))。


三、标准二分查找实现
场景:在有序数组中查找某个值是否存在。
int binarySearch(const vector<int>& nums, int target) {
    int left = 0, right = nums.size() - 1; // 闭区间 [left, right]
    while (left <= right) {                // 区间合法时循环
        int mid = left + (right - left) / 2; // 避免溢出
        if (nums[mid] == target) {
            return mid; // 找到目标,返回索引
        } else if (nums[mid] < target) {
            left = mid + 1; // 目标在右半区
        } else {
            right = mid - 1; // 目标在左半区
        }
    }
    return -1; // 未找到
}
关键点
  1. 循环条件left <= right,确保区间非空时继续搜索。

  2. 中间值计算:使用 mid = left + (right - left) / 2 而非 (left + right) / 2,避免整数溢出。

  3. 边界调整

    • left = mid + 1:排除左半区。

    • right = mid - 1:排除右半区。


四、变种问题与实现
1. 查找第一个等于目标的位置(Lower Bound)
int findFirst(const vector<int>& nums, int target) {
    int left = 0, right = nums.size() - 1;
    int res = -1;
    while (left <= right) {
        int mid = left + (right - left) / 2;
        if (nums[mid] >= target) {
            right = mid - 1; // 向左收缩,寻找左边界
            if (nums[mid] == target) res = mid;
        } else {
            left = mid + 1;
        }
    }
    return res;
}
2. 查找最后一个等于目标的位置(Upper Bound)
int findLast(const vector<int>& nums, int target) {
    int left = 0, right = nums.size() - 1;
    int res = -1;
    while (left <= right) {
        int mid = left + (right - left) / 2;
        if (nums[mid] <= target) {
            left = mid + 1; // 向右收缩,寻找右边界
            if (nums[mid] == target) res = mid;
        } else {
            right = mid - 1;
        }
    }
    return res;
}
3. 查找插入位置
int searchInsert(const vector<int>& nums, int target) {
    int left = 0, right = nums.size() - 1;
    while (left <= right) {
        int mid = left + (right - left) / 2;
        if (nums[mid] == target) {
            return mid;
        } else if (nums[mid] < target) {
            left = mid + 1;
        } else {
            right = mid - 1;
        }
    }
    return left; // 返回插入位置
}

五、STL中的二分查找

C++标准库提供了以下二分查找工具:

  1. std::binary_search:检查元素是否存在。

    bool exists = binary_search(nums.begin(), nums.end(), target);
  2. std::lower_bound:返回第一个 >= target 的迭代器。

    auto it = lower_bound(nums.begin(), nums.end(), target);
    int index = it - nums.begin();
  3. std::upper_bound:返回第一个 > target 的迭代器。

    auto it = upper_bound(nums.begin(), nums.end(), target);

六、常见问题与陷阱
  1. 死循环

    • 错误调整边界会导致循环无法终止,如 left = mid 而非 mid + 1

  2. 边界条件

    • 未正确处理 left 和 right 的更新。

  3. 溢出问题

    • 使用 (left + right) / 2 可能溢出,应改用 left + (right - left) / 2

  4. 未排序数组

    • 二分查找要求数组有序,否则结果不可预测。


七、应用场景
  1. 有序数组的快速查找。

  2. 寻找数值解(如平方根、方程根)。

  3. 旋转排序数组中的最小值(如 [4,5,6,7,0,1,2])。

  4. 山脉数组(先增后减)的峰值查找。


八、总结
  • 核心思想:分治策略,每次缩小一半搜索范围。

  • 关键点:循环终止条件、边界调整、中间值计算。

  • 适用条件:有序数组。

  • 变种问题:需根据具体需求调整边界条件和返回值。


http://www.kler.cn/a/535812.html

相关文章:

  • Chapter 4-1. Troubleshooting Congestion in Fibre Channel Fabrics
  • MVC 文件夹:架构之美与实际应用
  • K8s 分布式存储后端(K8s Distributed Storage Backend)
  • [NKU]C++安装环境 VScode
  • 在 Mac M2 上安装 PyTorch 并启用 MPS 加速的详细教程与性能对比
  • 19爬虫:使用playwright登录超级鹰
  • 深度学习篇---深度学习相关知识点关键名词含义
  • MySQL 缓存机制与架构解析
  • react的antd表单校验,禁止输入空格并触发校验提示
  • 【中间件】 Kafka
  • spring基础总结
  • 【kafka实战】04 Kafka生产者发送消息过程源码剖析
  • 深入浅出 NRM:加速你的 npm 包管理之旅
  • 图论- DFS/BFS遍历
  • Java面试汇总>>>初级工程师—面试1000题
  • CSV数据分析智能工具(基于OpenAI API和streamlit)
  • Netty之JavaNIO编程模型介绍01
  • 基于docker搭建Kafka集群,使用内部自带的Zookeeper方式搭建
  • Java进阶:Zookeeper相关笔记
  • E卷-螺旋数字矩阵-(100分)
  • langchain教程-3.OutputParser/输出解析
  • websocket自动重连封装
  • MyBatis核心配置文件详解:从层级关系到实战配置
  • Oh3.2项目升级到Oh5.0(鸿蒙Next)具体踩坑记录(一)
  • 如何打开vscode系统用户全局配置的settings.json
  • JS实现一个通用的循环填充数组的方法