二分查找右边界应设为nums.length还是nums.length-1?
二分查找的右边界初始值选择本质是搜索区间定义的区别,只要和边界收缩逻辑完全匹配就不会出问题,二者没有强制的使用场景要求,只需要全程遵循同一套规则即可。
1. 两种右边界对应的区间定义
- 右边界初始值为
r = nums.length - 1:对应左闭右闭区间[l, r],搜索范围包含r位置本身,r是合法的数组下标,所有循环中计算得到的中间值m都不会超过数组最大下标 - 右边界初始值为
r = nums.length:对应左闭右开区间[l, r),搜索范围只到r的前一个位置,r本身是不参与搜索的非法下标,所有逻辑中都不能直接访问nums[r]
你提供的第二版代码报错的核心原因是把两套规则混用:用了左闭右开的初始右边界,但收缩逻辑完全照搬左闭右闭的规则,当target大于数组所有元素时,计算出的中间值m会等于nums.length,直接访问nums[m]就会触发数组越界异常。
2. 两种写法的正确实现(以查找有序数组中target最后一次出现的下标为例)
2.1 左闭右闭写法(r = nums.length - 1)
这是你第一版的正确实现,逻辑更直观,不易出错:
public int binarySearchLarger(int[] nums, int target) { int l = 0; int r = nums.length - 1; while (l < r) { // 向上取整避免l和r相邻时进入死循环 int m = l + (r - l + 1) / 2; if (nums[m] <= target) { // m位置符合要求,保留为左边界,继续向右找更靠后的匹配项 l = m; } else { // m位置比target大,不可能是解,右边界移动到m-1 r = m - 1; } } return nums[l] == target ? l : -1; }
2.2 左闭右开写法(r = nums.length)
如果选择r = nums.length的初始值,必须配套修改边界收缩逻辑,不能访问右边界下标:
public int binarySearchLarger(int[] nums, int target) { int l = 0; int r = nums.length; while (l < r) { int m = l + (r - l) / 2; if (nums[m] > target) { // m位置不符合要求,右边界移动到m(开区间不包含m,无需减1) r = m; } else { // m位置符合要求,继续向右找更靠后的匹配项 l = m + 1; } } // 循环结束时l==r,最后一个匹配项的位置是l-1 l--; return l >= 0 && nums[l] == target ? l : -1; }
3. 选择建议
两种写法没有性能差异,根据个人编码习惯选择即可:
- 刚接触二分查找更推荐用左闭右闭写法,逻辑更贴合直觉,不容易出现越界问题
- 如果你习惯和Java
Arrays.binarySearch、Pythonbisect模块等内置二分方法的规则对齐,可以选择左闭右开写法
内容的提问来源于stack exchange,提问作者yueran zhang
相关产品推荐
相关产品推荐

