Hot 100 --- 搜索插入位置

本文概览:本文讲解搜索插入位置的核心思路:在有序数组中查找 target,找到返回索引,找不到返回应该插入的位置


一、题目


二、题目分析

这题的要求是:给定一个有序数组 和一个目标值 target,在数组中找到目标值并返回其索引。如果目标值不存在,则返回它应该被按顺序插入的位置。

比如数组 [1, 3, 5, 7]:

  • 查找 target = 3,返回索引 1
  • 查找 target = 4,返回插入位置 2(插入后数组为 [1, 3, 4, 5, 7])

这题可以用二分查找实现 O(log n) 的时间复杂度,前提是数组一定有序。

二分查找的思路就是:每次查询都查询目前查找范围的中间点,判断中间点的值大于还是小于 target。


思路概览

Java 实现代码如下

java 复制代码
public int searchInsert(int[] nums, int target) {
    int left = 0, right = nums.length - 1;
    while(left <= right) {
        int mid = left + (right - left) / 2;
        if(nums[mid] == target) {
            return mid;
        } else if(nums[mid] < target) {
            left = mid + 1;
        } else {
            right = mid - 1;
        }
    }
    return left;
}

思路简要说明:

  1. 初始范围 :left = 0,right = nums.length - 1
  2. 中间点计算 :mid = left + (right - left) / 2,避免溢出
  3. 范围更新 :小于 target 时 left = mid + 1,大于时 right = mid - 1
  4. 终止条件 :left > right 说明搜索完毕,返回 left 就是插入位置

三、思路详解

第一步:为什么需要有序数组

二分查找的核心是"每次排除一半的搜索范围"。如果数组无序,中间点的值无法告诉我们 target 在左边还是右边,也就无法排除一半。

比如升序数组 [1, 3, 5, 7, 9],查找 target = 7:

  • 中间点是 5,5 < 7,说明 7 一定在右边(因为数组升序,左边的值都小于 5)
  • 排除左半部分,搜索范围变为 [7, 9]
  • 中间点是 7,找到目标
第二步:中间点的计算

中间点的计算公式是:

java 复制代码
int mid = left + (right - left) / 2;

为什么不直接写 (left + right) / 2?

因为如果 left 和 right 都很大,left + right 可能溢出 int 的取值范围 。比如 left = 2^30,right = 2^30,相加就超过了 Integer.MAX_VALUE。

而 left + (right - left) / 2 是先算距离再除以 2,不会溢出。

第三步:范围更新

假设数组升序,每次查询中间点后:

  • numsmid == target:找到目标,直接返回
  • numsmid < target :target 一定在右边,更新 left = mid + 1
  • numsmid > target :target 一定在左边,更新 right = mid - 1

为什么是 mid + 1 和 mid - 1,不是 mid?

因为 mid 已经检查过了,不是目标值,所以下一次搜索范围不包含 mid。

第四步:终止条件和插入位置

循环条件是 left <= right,当 left > right 时退出循环。

这说明搜索范围为空,没有找到 target。

关键问题:为什么返回 left 就是插入位置?

当循环结束时,left = right + 1,此时有两种情况:

  1. 最后一步是 left = mid + 1 :说明 nums[mid] < target,mid 左边的值都小于 target,所以 target 应该插入到 mid + 1 的位置,也就是 left
  2. 最后一步是 right = mid - 1 :说明 nums[mid] > target,mid 右边的值都大于 target,所以 target 应该插入到 mid 的位置,而此时 left = mid(因为 right = mid - 1,循环结束意味着 left = right + 1 = mid)

所以无论哪种情况,返回 left 就是 target 应该插入的位置。

第五步:完整执行过程

例子 1:找到 target

以 nums = [1, 3, 5, 7, 9],target = 7 为例:

复制代码
初始:left=0, right=4

第 1 轮:
  mid = 0 + (4-0)/2 = 2
  nums[2] = 5 < 7
  left = mid + 1 = 3

第 2 轮:
  left=3, right=4
  mid = 3 + (4-3)/2 = 3
  nums[3] = 7 == 7
  返回 3 ✓

例子 2:找不到 target,返回插入位置

以 nums = [1, 3, 5, 7, 9],target = 6 为例:

复制代码
初始:left=0, right=4

第 1 轮:
  mid = 2, nums[2] = 5 < 6
  left = 3

第 2 轮:
  left=3, right=4
  mid = 3, nums[3] = 7 > 6
  right = 2

第 3 轮:
  left=3, right=2
  left > right,退出循环
  返回 left = 3

6 应该插入到索引 3 的位置,插入后数组为 [1, 3, 5, 6, 7, 9]。

例子 3:插入到末尾

以 nums = [1, 3, 5],target = 7 为例:

复制代码
初始:left=0, right=2

第 1 轮:
  mid = 1, nums[1] = 3 < 7
  left = 2

第 2 轮:
  left=2, right=2
  mid = 2, nums[2] = 5 < 7
  left = 3

第 3 轮:
  left=3, right=2
  left > right,退出循环
  返回 left = 3(插入到末尾)

例子 4:插入到开头

以 nums = [3, 5, 7],target = 1 为例:

复制代码
初始:left=0, right=2

第 1 轮:
  mid = 1, nums[1] = 5 > 1
  right = 0

第 2 轮:
  left=0, right=0
  mid = 0, nums[0] = 3 > 1
  right = -1

第 3 轮:
  left=0, right=-1
  left > right,退出循环
  返回 left = 0(插入到开头)
第六步:时间复杂度分析

每次搜索范围减半,所以时间复杂度是 O(log n)。

比如数组长度 1024,最多查 10 次(2^10 = 1024),而线性查找最多要查 1024 次。


四、总结

搜索插入位置的核心是三个要素:

  1. 有序数组:这是前提
  2. 中间点计算 :left + (right - left) / 2 避免溢出
  3. 范围更新:根据中间点和 target 的大小关系,排除一半的搜索范围
  4. 插入位置 :循环结束时返回 left,就是 target 应该插入的位置

这个思路可以推广到所有"有序数组查找和插入"类题目。

相关推荐
vx_Biye_Design2 小时前
springboot一站式旅游管理平台81037-计算机课程设计、毕业设计
java·vue.js·spring boot·后端·课程设计·express·旅游
写后端的胖头鱼2 小时前
时间复杂度 & 空间复杂度
java·数据结构·算法·时间复杂度·空间复杂度
多弗朗皮卡丘2 小时前
数据结构番外:环形队列
数据结构·c
hahaha60162 小时前
黑体轨迹和色温--光源白点
人工智能·嵌入式硬件·算法·计算机视觉
嵌入式学习菌2 小时前
ESP32 ModbusTCP 分片缓存
java·后端·spring
海绵宝宝转agent2 小时前
基于Redis ZSet+AOP+注解实现限流注解算法
数据库·redis·算法
vx_Biye_Design2 小时前
springboot宠物寄养服务预约与监管系统82684-计算机课程设计、毕业设计
java·vue.js·spring boot·elasticsearch·课程设计·express·宠物
程序员小杰@2 小时前
Spring Boot 常用注解分类速记
java·spring boot·后端
vx_Biye_Design2 小时前
springboot小学生英语学习APP62773-计算机课程设计、毕业设计
java·vue.js·spring boot·后端·python·学习·课程设计
吴声子夜歌2 小时前
Nginx应用与运维——Nginx日志管理
java·运维·nginx