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。

(完美符合逻辑)

相关推荐
我命由我123451 小时前
Android 多进程开发 - AIDL 回调、RemoteCallbackList、AIDL 安全校验
android·java·安全·android studio·安卓·android-studio·android runtime
一个有梦有戏的人1 小时前
Java 网络编程核心:BIO、NIO、AIO IO 模型深度解析与实战
java·网络·后端·netty·nio
载数而行5201 小时前
算法系列5之交换排序
c语言·数据结构·c++·算法·排序算法
这波不该贪内存的1 小时前
Linux文件编程:流与操作全解析
java·服务器·前端
Frostnova丶1 小时前
(1)LeetCode 1. 两数之和
leetcode·哈希算法
昱宸星光1 小时前
Xnio源码分析
java·jvm·spring
黑白极客1 小时前
ACP大模型认证刷题工具开源,助力高效备考
java·ai·github·llama·认证
程序员南飞1 小时前
算法笔试-求一个字符串的所有子串
java·开发语言·数据结构·python·算法·排序算法
MyY_DO1 小时前
你应该有属于自己的作品
java