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

二分查找易错点分析报告

二分查找易错点分析报告


1. 引言

二分查找(Binary Search)是一种高效的查找算法,时间复杂度为 (O(\log n)),常用于在有序数组中查找目标值。然而,二分查找的实现细节非常关键,稍有不慎就会导致错误。本文总结了二分查找的常见易错点,并结合代码示例进行分析。


2. 二分查找的基本原理

二分查找的核心思想是通过不断缩小搜索范围来快速定位目标值。其基本步骤如下:

  1. 初始化左右边界 lr
  2. 计算中间位置 mid
  3. 比较 mid 处的值与目标值:
    • 如果相等,返回 mid
    • 如果 mid 处的值大于目标值,缩小右边界 r = mid - 1
    • 如果 mid 处的值小于目标值,缩小左边界 l = mid + 1
  4. 重复上述步骤,直到 l > r,表示未找到目标值。

3. 二分查找的易错点
3.1 终止条件错误
  • 问题描述
    终止条件设置不当,可能导致循环无法正确结束或遗漏目标值。
  • 错误示例
    while (l + 1 != r) {
        int mid = (l + r) >> 1;
        if (a[mid] >= target) r = mid;
        else l = mid;
    }
    
    • 这种终止条件在某些情况下无法正确找到目标值,例如当目标值在边界时。
  • 正确做法
    while (l <= r) {
        int mid = (l + r) >> 1;
        if (a[mid] == target) return mid;
        else if (a[mid] < target) l = mid + 1;
        else r = mid - 1;
    }
    
3.2 中间值计算错误
  • 问题描述
    中间值 mid 的计算方式可能导致溢出或死循环。
  • 错误示例
    int mid = (l + r) / 2;
    
    • lr 都很大时,l + r 可能溢出。
  • 正确做法
    int mid = l + (r - l) / 2;
    
3.3 边界更新错误
  • 问题描述
    边界更新方式不当,可能导致搜索范围无法缩小或遗漏目标值。
  • 错误示例
    if (a[mid] >= target) r = mid;
    else l = mid;
    
    • 这种更新方式可能导致死循环,例如当 lr 相邻时。
  • 正确做法
    if (a[mid] >= target) r = mid - 1;
    else l = mid + 1;
    
3.4 返回值错误
  • 问题描述
    返回值设置不当,可能导致返回错误的位置。
  • 错误示例
    return r;
    
    • 在某些情况下,r 可能不是目标值的位置。
  • 正确做法
    return l; // 或根据具体需求返回
    
3.5 查找目标不明确
  • 问题描述
    查找目标不明确,例如查找第一个大于等于目标值的位置时,逻辑错误。
  • 错误示例
    if (a[mid] >= target) r = mid;
    else l = mid;
    
    • 这种逻辑可能导致无法找到第一个满足条件的位置。
  • 正确做法
    if (a[mid] >= target) r = mid;
    else l = mid + 1;
    

4. 正确实现示例
4.1 查找目标值
int binary_search(int a[], int n, int target) {
    int l = 0, r = n - 1;
    while (l <= r) {
        int mid = l + (r - l) / 2;
        if (a[mid] == target) return mid;
        else if (a[mid] < target) l = mid + 1;
        else r = mid - 1;
    }
    return -1; // 未找到
}
4.2 查找第一个大于等于目标值的位置
int lower_bound(int a[], int n, int target) {
    int l = 0, r = n - 1;
    while (l < r) {
        int mid = l + (r - l) / 2;
        if (a[mid] >= target) r = mid;
        else l = mid + 1;
    }
    return l;
}
4.3 查找最后一个小于等于目标值的位置
int upper_bound(int a[], int n, int target) {
    int l = 0, r = n - 1;
    while (l < r) {
        int mid = l + (r - l + 1) / 2;
        if (a[mid] <= target) l = mid;
        else r = mid - 1;
    }
    return l;
}

5. 总结

二分查找的实现细节非常重要,常见的易错点包括终止条件、中间值计算、边界更新和返回值设置。通过理解这些易错点并掌握正确的实现方法,可以有效避免错误,提高代码的鲁棒性和效率。


6. 建议
  1. 理解原理:深入理解二分查找的核心思想,避免盲目套用模板。
  2. 测试边界条件:在实现二分查找时,务必测试各种边界条件,例如目标值在数组开头、结尾或不存在的情况。
  3. 使用标准模板:尽量使用经过验证的标准模板,减少出错的可能性。

【附加】理解查找第一个大于等于目标值的位置和查找最后一个小于等于目标值的位置

在二分查找中,除了查找目标值的精确位置外,还经常需要查找满足某些条件的位置,例如:

  1. 第一个大于等于目标值的位置lower_bound)。
  2. 最后一个小于等于目标值的位置upper_bound)。

这两种操作在实际应用中非常常见,例如在有序数组中插入元素或查找范围时。以下是详细解释和实现方法。


1. 查找第一个大于等于目标值的位置(lower_bound

定义:

在有序数组中,找到第一个大于或等于目标值的位置。如果目标值存在,则返回其第一个出现的位置;如果目标值不存在,则返回第一个大于目标值的位置。

应用场景:
  • 在有序数组中插入元素时,找到插入位置。
  • 查找某个范围的起始位置。
实现思路:
  1. 初始化左右边界 lr
  2. 在循环中计算中间位置 mid
  3. 如果 a[mid] >= target,则目标位置可能在 [l, mid] 区间,更新 r = mid
  4. 如果 a[mid] < target,则目标位置可能在 [mid + 1, r] 区间,更新 l = mid + 1
  5. 循环结束后,l 即为第一个大于或等于目标值的位置。
代码实现:
int lower_bound(int a[], int n, int target) {
    int l = 0, r = n - 1;
    while (l < r) {
        int mid = l + (r - l) / 2;
        if (a[mid] >= target) r = mid;
        else l = mid + 1;
    }
    return l; // 返回第一个大于或等于目标值的位置
}
示例:
int a[] = {1, 3, 5, 7, 9};
int target = 6;
int pos = lower_bound(a, 5, target); // 返回 3(第一个大于等于 6 的位置)

2. 查找最后一个小于等于目标值的位置(upper_bound

定义:

在有序数组中,找到最后一个小于或等于目标值的位置。如果目标值存在,则返回其最后一个出现的位置;如果目标值不存在,则返回最后一个小于目标值的位置。

应用场景:
  • 在有序数组中查找某个范围的结束位置。
  • 查找某个值的前驱位置。
实现思路:
  1. 初始化左右边界 lr
  2. 在循环中计算中间位置 mid
  3. 如果 a[mid] <= target,则目标位置可能在 [mid, r] 区间,更新 l = mid
  4. 如果 a[mid] > target,则目标位置可能在 [l, mid - 1] 区间,更新 r = mid - 1
  5. 循环结束后,l 即为最后一个小于或等于目标值的位置。
代码实现:
int upper_bound(int a[], int n, int target) {
    int l = 0, r = n - 1;
    while (l < r) {
        int mid = l + (r - l + 1) / 2; // 注意:向上取整
        if (a[mid] <= target) l = mid;
        else r = mid - 1;
    }
    return l; // 返回最后一个小于或等于目标值的位置
}
示例:
int a[] = {1, 3, 5, 7, 9};
int target = 6;
int pos = upper_bound(a, 5, target); // 返回 2(最后一个小于等于 6 的位置)

3. 两者区别

操作目标更新条件返回值意义
lower_bound第一个大于等于目标值的位置a[mid] >= targetr = mid第一个大于或等于目标值的位置
upper_bound最后一个小于等于目标值的位置a[mid] <= targetl = mid最后一个小于或等于目标值的位置

4. 总结

  1. lower_bound

    • 用于查找第一个大于或等于目标值的位置。
    • 实现时注意更新 r = midl = mid + 1
  2. upper_bound

    • 用于查找最后一个小于或等于目标值的位置。
    • 实现时注意更新 l = midr = mid - 1,并且 mid 需要向上取整。
  3. 应用场景

    • lower_boundupper_bound 可以用于查找范围、插入位置或前驱后继位置。

通过理解这两种操作的实现思路和区别,可以更好地应用二分查找解决实际问题。


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

相关文章:

  • Linux软件包管理与Vim编辑器指南
  • rust学习笔记13-18. 四数之和
  • 【Spring】@PostConstruct详解
  • Conda:CondaSSLError
  • LabVIEW VI Scripting实现连接器窗格自动化
  • varchar (255) varchar (2550) 在mysql中实际占的空间会是十倍吗
  • MySQL的安装、备份还原及主从同步
  • java设计模式之桥接模式
  • 深度学习GRU模型原理
  • Linux——Shell运行原理以及Linux权限
  • 【Linux docker 容器】关于想要让虚拟机在开机时候也docker自己启动,容器也自己启动,省去要自己开docker和容器
  • 已安装 MFC 仍提示“此项目需要 MFC 库”的解决方法 (MSB8041)
  • 骑士74CMS_v3.34.0SE版uniapp全开源小程序怎么编译admin和member流程一篇文章说清楚
  • 【Go语言圣经1.5】
  • 前端对话框项目——调用字节Coze API
  • 18 | 实现简洁架构的 Handler 层
  • python str repr方法区别
  • 数据库原理4
  • 开源链动2+1模式AI智能名片S2B2C商城小程序在KOC参与门店做透中的应用探索
  • 本地部署资源聚合搜索神器 Jackett 并实现外部访问