sort.Search是Go标准库最稳妥的二分查找入口,但查相等值需两步:先用a[i] >= target找左边界,再验证idx < len(a)且a[idx] == target;直接用a[i] == target会导致返回插入点或panic。

sort.Search 是标准库提供的二分入口,但直接用它查“相等”会出错——它不找值,只找第一个满足条件的位置。你得自己把语义转成 a[i] >= target,再手动验证。
用 sort.Search 查相等值必须两步走
很多人写成 sort.Search(len(a), func(i int) bool { return a[i] == target }),这会导致:找不到时返回插入点(比如 []int{1,3,5} 查 4,返回索引 2),但 a[2] 是 5,≠4;更糟的是,当 i == len(a) 时闭包里访问 a[i] 会 panic。
- 正确断言只能是
a[i] >= target(升序前提下) - 调完必须显式检查:
if idx - 空切片、全小于、全大于 target 都会返回合法索引(0 或
len(a)),不判就崩 - 想省事用
sort.SearchInts(a, target)?它只是sort.Search的薄封装,底层完全一样
binarySearch 迭代版怎么写才不容易错
手写推荐闭区间 [left, right],循环条件是 left ,退出时 <code>left > right 就代表真没找到。
- 中点必须写成
left + (right - left) / 2,不是(left + right) / 2——后者在极端偏移或大内存映射场景可能整数溢出 - 比较后移动规则简单统一:
a[mid] > target → right = mid - 1;a[mid] < target → left = mid + 1 - 命中即返,不用额外状态变量;返回前别忘检查越界(虽然循环已控,但逻辑清晰更重要)
找重复元素的左右边界怎么改
基础版一找到就返回,但业务常要“第一个 3”或“最后一个 3”。本质还是二分,只是命中时不急着停,而是继续缩区间。
立即学习“go语言免费学习笔记(深入)”;
- 找左边界:命中时执行
right = mid - 1,最后检查left是否越界及a[left] == target - 找右边界:命中时执行
left = mid + 1,最后检查right是否越界及a[right] == target - 降序数组不能直接套:要么翻转断言为
a[i] <= target,要么先转升序——sort.Search不支持降序语义
递归版能不能用,哪些坑最致命
递归语义清楚,但生产环境不推荐。Go 默认栈深度有限,且每次调用有开销;更隐蔽的问题是:容易忽略返回值传递。
- 常见错误:递归调用写了
BinarySearch(...)却没接返回值,结果永远返回零值 - 参数必须带
low、high,别传子切片(会复制底层数组,O(n) 开销) - 终止条件是
low > high,不是len(arr) == 0(后者低效且易错) - 哪怕数据量百万级(log₂(1e6) ≈ 20 层),递归也无性能优势,纯属增加 panic 风险
所有二分都依赖升序前提,这点一旦漏验,后续全是幻觉。不是“差不多有序”就行,是必须严格单调升序——连等于号都不能乱加。


















