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

二分查找易错点分析报告

二分查找易错点分析报告


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) {//注意这个upper_bound和标准库中的upper_bound函数是不同的,标准库中返回的的第一个大于目标值的地址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.mrgr.cn/news/94265.html

相关文章:

  • 图论·拓扑排序
  • 多数元素——面试经典150题(力扣)
  • Leetcode 刷题笔记1 动态规划part08
  • 【原创】springboot+vue智能办公管理系统设计与实现
  • 学习springboot-Bean管理(Bean 注册,Bean 扫描)
  • ESP-IDF ubuntu版本 V5.2
  • react实现一个列表的拖拽排序(react实现拖拽)
  • Kotlin和Java区别
  • 达梦主备集群部署
  • 阿里云操作系统控制台评测:国产AI+运维 一站式运维管理平台
  • ROS实践(四)机器人SLAM建图(gmapping)
  • 推理框架SGLang安装与调试
  • LVS + Keepalived 高可用集群
  • 《YOLOE: Real-Time Seeing Anything》论文速览翻译,支持文本提示,视觉提示等开放世界检测算法!
  • Java常见的并发设计模式
  • maven wrapper的使用
  • 爬虫中一些有用的用法
  • Qt:绘图API
  • 【Pytorch Transformers Fine-tune】使用BERT进行情感分类任务微调
  • Selenium 自动化测试学习总结