【简单】35. 搜索插入位置

0 阅读3分钟

给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。

请必须使用时间复杂度为 O(log n) 的算法。

示例 1:

输入: nums = [1,3,5,6], target = 5
输出: 2

示例 2:

输入: nums = [1,3,5,6], target = 2
输出: 1

示例 3:

输入: nums = [1,3,5,6], target = 7
输出: 4

提示:

  • 1 <= nums.length <= 104
  • -104 <= nums[i] <= 104
  • nums 为 无重复元素 的 升序 排列数组
  • -104 <= target <= 104

1. 生活案例:在图书馆书架插书

想象你在图书馆管理一个已经按编号排好序的书架:

  • 规则:书架上的书从左到右编号越来越大。你手里有一本新书,编号是 target

  • 任务

    1. 如果书架上已经有这本书了,直接找到它的位置。
    2. 如果没有这本书,你要找到它应该插入的位置,保证插入后书架依然是有序的。
  • 过程

    • 你来到书架中间(mid),发现中间的书编号比你的大,于是你去左半边找;如果比你的小,就去右半边找。
    • 重点来了:当你左右找遍了也没发现这编号,你最后停下来的那个地方,就是你把新书挤进去的位置。

2. 代码实现与详细注释

这是你图片中的代码,我为你添加了详细的中文注释。注意看最后 return left 的奥秘:

JavaScript

/**
 * @param {number[]} nums - 已经排好序的书架
 * @param {number} target - 你手里书的编号
 * @return {number} - 找到的位置或应该插入的位置
 */
var searchInsert = function(nums, target) {
    let left = 0;
    let right = nums.length - 1;

    // 标准二分查找模板
    while (left <= right) {
        // 取中间点,防止溢出的写法
        let mid = Math.floor(left + (right - left) / 2);

        if (nums[mid] === target) {
            // 1. 运气好,书架上刚好有这编号,直接返回位置
            return mid;
        } else if (nums[mid] < target) {
            // 2. 中间的书编号小了,说明你要找的位置在右边
            left = mid + 1;
        } else {
            // 3. 中间的书编号大了,说明你要找的位置在左边
            right = mid - 1;
        }
    }

    // 4. 【核心点】:循环结束还没找到。
    // 此时 left 指向的位置,正好就是这个数字“应该在”的位置。
    // 比如在 [1, 3, 5, 6] 中找 2,循环结束时 left 会停在索引 1 的位置。
    return left;
};

3. 核心原理解析

为什么找不到时返回 left

这是二分查找的一个神奇性质。当 while (left <= right) 结束时:

  • right 会停在比 target 小的最后一个元素上。
  • left 会停在比 target 大的第一个元素上。
  • 既然我们要插入 target 且保持有序,它理所当然应该排在“比它大的第一个元素”的位置上,把原来的元素往后挤。所以 return left 就是我们要的答案。

各种情况演示:

nums = [1, 3, 5, 6] 为例:

  1. target = 5:直接在 mid 匹配到,返回 2

  2. target = 2

    • 第一次:mid=3,3 > 2,往左找,right 变成索引 0。
    • 第二次:mid=1,1 < 2,往右找,left 变成索引 1。
    • 此时 left > right,退出,返回 left (1)
  3. target = 7(插在最后):left 会一路增加到索引 4,返回 4。

  4. target = 0(插在最前):right 会一路减小到 -1,返回 left (0)

复杂度分析

  • 时间复杂度O(logn)O(\log n)。每次比较都排除掉一半的范围。
  • 空间复杂度O(1)O(1)。只用了两个指针。