35. 搜索插入位置

35. 搜索插入位置

简单

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

请必须使用时间复杂度为 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

📝 核心笔记:搜索插入位置 (Search Insert Position)

1. 核心思想 (一句话总结)

"寻找第一个大于等于 Target 的位置。"

lowerBound 的定义是:在一个有序数组中,找到第一个满足 nums[i] >= target 的下标 i。

  • 如果 target 存在,它返回第一个 target 的下标。
  • 如果 target 不存在,它返回 target 应当插入的位置(保持数组有序)。
2. 算法流程 (左闭右开版)
  1. 区间定义[left, right)
    • left = 0
    • right = nums.length (注意不是 length-1,因为区间右边是开的,取不到 length)。
  1. 循环条件left < right
    • 因为是左闭右开,当 left == right 时,区间变成 [k, k),里面没元素了,循环自然结束。
  1. 区间收缩 (关键)
    • 太小了 ( nums[mid] < target**)** :target 肯定在右边,且 mid 肯定不是答案。
      • left = mid + 1 (左闭,跳过 mid)。
    • 够大了 ( nums[mid] >= target**)** :mid 可能是答案,也可能答案在更左边。
      • right = mid (右开,把 mid 设为右边界,下一次搜索范围 ... ~ mid-1)。
🔍 代码回忆清单 (带注释版)
复制代码
// 题目:LC 35. Search Insert Position
class Solution {
    public int searchInsert(int[] nums, int target) {
        return lowerBound2(nums, target);
    }

    // 左闭右开区间写法 [left, right)
    private int lowerBound2(int[] nums, int target) {
        int left = 0;
        int right = nums.length; // ⚠️ 注意 1: 右边界是 length (取不到)
        
        // ⚠️ 注意 2: 条件是 < 不是 <=
        // 当 left == right 时,区间 [left, right) 为空,退出循环
        while (left < right) { 
            // 防止溢出的中间位置计算
            int mid = left + (right - left) / 2;
            
            if (nums[mid] < target) {
                // 目标在右边,mid 肯定不是,+1 跳过
                left = mid + 1; // 范围缩小到 [mid+1, right)
            } else {
                // nums[mid] >= target
                // 目标在左边,或者就是 mid
                // 因为是右开区间,right = mid 代表下一次搜索不包含 mid
                right = mid; // 范围缩小到 [left, mid)
            }
        }
        // 结束时 left == right,返回谁都行
        return left; 
    }
}
⚡ 快速复习 CheckList (易错点)

|---------------|-----------------------|------------------------|
| 维度 | 左闭右开 [L, R) (推荐) | 左闭右闭 [L, R] (常见) |
| Right 初始化 | nums.length | nums.length - 1 |
| While 条件 | left < right | left <= right |
| 收缩 Right | right = mid | right = mid - 1 |
| 终止状态 | left == right | left == right + 1 |
| 思维负担 | (无需考虑 mid-1 ) | 中 (需考虑越界和漏查) |

  • \] **为什么是** **right = mid****?**

    • 如果 nums[mid] >= target,说明 mid 可能是目标位置。
    • 因为区间是 右开 的,我们把 right 设为 mid,意味着下一轮搜索区间是 [left, mid)
    • 这样下一轮确实排除了 mid (因为它取不到),但逻辑上 mid 并没有被丢弃,因为如果最后 left 追上来撞到 right (也就是撞到 mid),循环结束,mid 就成了答案。
  • \] **最大值越界?**

    • 如果数组所有数都 < targetleft 会一直 +1,最后变成 nums.length
    • 这也是符合预期的(插在数组末尾),不会产生 ArrayIndexOutOfBounds,因为我们只返回下标,不访问 nums[left]
🖼️ 数字演练

数组 nums = [1, 3, 5, 6], target = 2

  1. Init : L=0, R=4. 区间 [0, 4).
    • mid = 2 (val=5).
    • 5 >= 2 (大了)。
    • Right = 2 。区间变 [0, 2).
  1. Loop 2 : L=0, R=2.
    • mid = 1 (val=3).
    • 3 >= 2 (大了)。
    • Right = 1 。区间变 [0, 1).
  1. Loop 3 : L=0, R=1.
    • mid = 0 (val=1).
    • 1 < 2 (小了)。
    • Left = 1 。区间变 [1, 1).
  1. End : L == R (均为 1),退出。
    • 返回 1
    • 验证:2 确实应该插在 13 之间,下标是 1。

(完美符合逻辑)

相关推荐
是Yu欸几秒前
LangGraph 智能体状态管理与决策
java·javascript·数据库
计算机学姐几秒前
基于SpringBoot的中药材店铺管理系统
java·vue.js·spring boot·后端·spring·tomcat·推荐算法
猫墨*1 分钟前
springboot3、knife4j-openapi3配置动态接口版本管理
java·开发语言
愣头不青3 分钟前
543.二叉树的直径
java·算法
此方ls4 分钟前
机器学习聚类算法二——DBSCAN(Density-Based Spatial Clustering of Applications with Noise)
算法·机器学习·聚类
add45a8 分钟前
C++中的原型模式
开发语言·c++·算法
2401_844221329 分钟前
C++类型推导(auto/decltype)
开发语言·c++·算法
2201_7538777910 分钟前
高性能计算中的C++优化
开发语言·c++·算法
hans汉斯10 分钟前
基于区块链和语义增强的科研诚信智能管控平台
人工智能·算法·yolo·数据挖掘·区块链·汉斯出版社
2501_9454251510 分钟前
分布式系统容错设计
开发语言·c++·算法