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

目录

一、核心思想

二、前提条件

三、标准二分查找实现

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

关键点:

四、变种问题与实现

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& 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& 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& 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& 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. 山脉数组(先增后减)的峰值查找。


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

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

  • 适用条件:有序数组。

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

你可能感兴趣的:(算法,算法,数据结构,c++,c语言,开发语言,leetcode,排序算法)