binarysearch必须要求数组有序,因为其核心逻辑依赖中间值划分有序区间;无序时排除一半数据无效,降序或部分有序需额外处理;防溢出应使用mid = left + (right - left) / 2;返回值设计兼顾存在性与插入点,重复元素时位置不固定。

为什么 binarySearch 一定要求数组有序?
因为算法每一步都依赖“中间值能划分左右区域”的逻辑:如果 arr[mid] > target,它就默认所有右边的数都更大——这只有在升序时才成立。一旦无序,mid 左右两边毫无规律,排除一半数据就成了瞎猜。
- 降序数组不能直接用标准
binarySearch,要么先反转,要么改比较逻辑(比如把target 改成 <code>target > arr[mid]) - 部分有序(如旋转数组)需先定位断点,再分段二分,不能跳过预处理
- Java 的
Collections.binarySearch()和 Python 的bisect模块都明确要求输入已排序,否则返回值无意义,甚至越界
mid = left + (right - left) / 2 而不是 (left + right) / 2
这是防整数溢出的关键细节。当 left 和 right 都接近 Integer.MAX_VALUE(约 21 亿)时,left + right 会溢出变负数,导致 mid 错误,进而索引越界或死循环。
- 几乎所有现代实现(JDK、Go
sort.Search、Rustslice::binary_search)都用减法形式 - 在 C/C++ 中尤其危险;Java 8+ 的
Arrays.binarySearch内部也如此实现 - 即使当前数据量小,也建议统一写法,避免未来迁移或边界扩展时翻车
查不到时返回 -1 还是 -(insertionPoint + 1)?
返回 -1 是最简语义(“没找到”),但很多场景需要知道“它该插在哪”。比如维护有序列表、计算排名、找前驱后继——这时返回插入位置的补码更实用。
- Java 的
Arrays.binarySearch()就是后者:若返回负值r,则插入点为-r - 1 - Python
bisect.bisect_left()直接返回插入索引,不加转换,语义更直白 - 自己实现时,若只关心“是否存在”,返回
-1足够;若后续要插入或求 rank,务必保留插入点信息
重复元素时 binarySearch 返回哪个位置?
标准实现不保证返回第一个或最后一个——它只保证返回“某个匹配位置”。比如数组 [2, 4, 4, 4, 6] 查 4,可能返回索引 1、2 或 3,取决于中间点怎么落。
- 要找第一次出现位置,得用左边界二分:
while (left ,并保持 <code>arr[mid] >= target时收缩右边界 - 要找最后一次出现位置,用右边界二分:
while (left ,条件改为 <code>arr[mid] - Java 的
Arrays.binarySearch不提供边界变体;需手写或借助Arrays.binarySearch配合while向左/右扫描(但最坏退化为 O(n))
实际写的时候,最容易漏掉的是:没验证输入是否真有序,以及混淆了“查找存在性”和“查找插入点”两种语义。这两个点不靠测试很难暴露,一上线就静默错。










