LC 35 搜索插入位置:二分查找谁都会,边界条件谁写谁懵

刷二分查找的第一天,我信心满满。

不就是每次砍一半吗?有手就行。

结果这道题我提交了四次才过,全栽在边界条件上。

你是不是也这样,感觉自己懂二分,但一写就错,错了还不知道为啥?

今天就聊聊这道题,以及我踩过的那些坑。

题目说的啥

给定一个升序排列的数组和一个目标值。

找到了就返回索引,没找到就返回它应该插入的位置。

要求时间复杂度 O(log n) ------ 说白了就是不让你从头到尾遍历,逼你用二分。

举个例子:nums = [1,3,5,6], target = 2,输出是 1,因为 2 应该插在 3 前面。

我一开始的想法

说实话,刚看到这题我第一反应是:

遍历一遍不就完了?找到第一个大于等于 target 的位置,返回它的索引。

javascript 复制代码
var searchInsert = function(nums, target) {
  for (let i = 0; i < nums.length; i++) {
    if (nums[i] >= target) return i
  }
  return nums.length
};

写完我还挺得意,多简洁啊。

然后一看题目要求:O(log n) 。

行吧,暴力解法虽然能过,但不符合题意。而且面试的时候你这么写,面试官大概率会追问:"能不能优化一下?"

我是怎么想到二分的

你想啊,数组是有序 的,还要求O(log n) ------ 这两个条件放在一起,答案基本就写在脸上了:二分查找。

但问题来了,普通的二分查找是 "找到了返回索引,找不到返回 -1"。

这道题不一样,找不到的时候要返回插入位置。

这个插入位置到底是啥?说白了就是:第一个大于等于 target 的元素的索引。

想通了这一点,二分的思路就清晰了。

手算一遍找找感觉

拿 nums = [1,3,5,6], target = 2 来走一遍。

一开始 l = 0, r = 3,中间位置 mid = 1,对应的值是 3。

3 比 2 大,说明 target 在左边,r 往左移:r = 0。

现在 l = 0, r = 0,mid = 0,对应的值是 1。

1 比 2 小,说明 target 在右边,l 往右移:l = 1。

这时候 l > r 了,循环结束。

返回谁?返回 l,也就是 1------ 正好是正确答案。

哎?为什么返回 l 而不是 r?

这个问题我当时想了好久。

关键:为什么返回 l

我教你一个记住的办法:

循环结束的时候,l 和 r 的关系是 l = r + 1。

你想啊,每次循环我们都在缩小范围:

  • 中间值比 target 小 → target 在右边 → l = mid + 1
  • 中间值大于等于 target → target 在左边 → r = mid - 1

当循环结束时,l 指向的就是第一个大于等于 target 的位置。

而 r 指向的是最后一个小于 target 的位置。

所以插入位置当然是 l 呀。

不信你可以再试几个例子,保证都对。

代码实现

javascript 复制代码
var searchInsert = function(nums, target) {
    // 左右指针,分别指向数组首尾
    let l = 0, r = nums.length - 1

    // 注意这里是 <=,不是 <
    // 写成 < 的话,l 和 r 重合的那个元素就没被检查到
    while (l <= r) {
        // 用位运算右移一位代替除以2再取整,效果一样但更装逼
        // 等价于 Math.floor((l + r) / 2)
        const mid = (l + r) >> 1

        if (nums[mid] < target) {
            // 中间值比目标小,目标肯定在右边
            // l 直接跳到 mid + 1,mid 本身已经排除了
            l = mid + 1
        } else {
            // 中间值 >= 目标,目标在左边(或者就是 mid 本身)
            // 为什么不用单独处理等于的情况?
            // 因为就算找到了,继续往左缩也不影响最终结果
            // 最后 l 还是会停在正确位置上
            r = mid - 1
        }
    }

    // 循环结束时 l = r + 1
    // l 指向第一个 >= target 的位置,正好就是插入位置
    // 别问我为什么知道,问就是在这里卡了半小时
    return l
};

我踩过的坑

说几个我自己犯过的错,看看你有没有中招。

第一个坑:循环条件写成 l < r。

这样写的话,当 l 和 r 指向同一个元素时,循环就结束了,那个元素根本没被检查过。结果就是有时候会漏掉正确答案。

第二个坑:r 初始值写成 nums.length。

也不是不行,但对应的边界条件全要改,很容易搞混。我推荐统一用 nums.length - 1,配合 l <= r,最不容易错。

第三个坑:返回 r。

我第一次写的时候想当然返回了 r,结果一跑全错。后来才想明白,插入位置是 l 指向的地方。

复杂度分析

时间复杂度 O (log n),每次砍掉一半,经典二分。

空间复杂度 O (1),就几个变量。

最后说两句

这道题看起来简单,但真的很考验你对二分查找边界的理解。

我当时刷完的感受是:二分查找不在于 "会不会",而在于 "能不能一次写对"。

建议你把这道题的模板背下来,后面刷二分的题目基本都能套用。

你有没有在二分查找的边界条件上翻过车?或者你有更巧妙的写法?评论区聊聊,我会一条一条看的。

如果觉得有帮助,点个赞让更多人看到,下次刷二分就不会懵了~

相关推荐
奋发向前wcx10 小时前
CSP-J复赛模拟赛4 王晨旭补题 2026.10.5
算法
可乐鸡翅yeah_11 小时前
FFmpeg 生成 HLS 独立分片 independent‑segments 参数到底有什么用
javascript·ffmpeg·音视频·safari·m3u8
福兮说11 小时前
canvas 旋转图片的六个坑:四角被裁、Math.ceil 多出 1px 黑边、JPG 角发黑、翻转方向反了
前端·javascript·图像处理·canvas
念何架构之路13 小时前
zap采样器与性能优化内幕
算法·性能优化·哈希算法
Ai-_Man13 小时前
您您这可以把Grok的多个会话比如说。左侧的多个会话一次性导出吗?不是单条会话里面的多次会对话。AI导出鸭
javascript·人工智能·ai·小程序·电脑
liangshanbo121513 小时前
JavaScript 异步核心主线
javascript·事件循环·异步操作
晒太羊的猫13 小时前
hot100动态规划刷题
算法·动态规划
weixin_5310946914 小时前
heap_4内存管理
java·开发语言·算法
余额瞒着我当琳14 小时前
红黑树的实现--红黑树的概念、规则、效率、结构、旋转、查找、验证
数据结构·c++·算法
程序员南飞14 小时前
win系统安装minio
java·数据结构·算法