【Leetcode hot 100】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
问题解答
核心思路:二分查找的区间定义
二分查找的本质是通过不断缩小「有效查找区间」逼近目标,需在整个过程中保持区间定义不变(循环不变量)。常见两种区间:
- 左闭右闭区间 [left, right]:区间的左右边界均包含在查找范围内。
- 左闭右开区间 [left, right):区间左边界包含,右边界不包含。
1. 左闭右闭区间 [left, right]
逻辑分析
- 初始化:
left = 0
(左边界),right = nums.length - 1
(右边界,因包含最后一个元素)。 - 循环条件:
left <= right
(当left == right
时,区间仍有1个元素,需判断是否为目标)。 - 指针移动:
- 若
nums[mid] > target
:目标在左区间,缩小右边界为right = mid - 1
(mid 已排除)。 - 若
nums[mid] < target
:目标在右区间,缩小左边界为left = mid + 1
(mid 已排除)。 - 若
nums[mid] == target
:直接返回 mid(找到目标)。
- 若
- 未找到目标:循环结束时
right < left
,right
指向「比 target 小的最后一个元素」,插入位置为right + 1
。
Java 代码
class Solution {public int searchInsert(int[] nums, int target) {int left = 0; // 左边界int right = nums.length - 1; // 右边界(左闭右闭,包含最后一个元素)// 循环条件:left <= right(区间内有元素时继续查找)while (left <= right) {// 计算 mid:避免 (left + right) 溢出(等价于 (left + right) / 2)int mid = left + (right - left) / 2;if (nums[mid] > target) {// 目标在左区间,缩小右边界(mid 已排除)right = mid - 1;} else if (nums[mid] < target) {// 目标在右区间,缩小左边界(mid 已排除)left = mid + 1;} else {// 找到目标,直接返回索引return mid;}}// 未找到目标:插入位置 = 比 target 小的最后一个元素的下一位(right + 1)return right + 1;}
}
2. 左闭右开区间 [left, right)
逻辑分析
- 初始化:
left = 0
(左边界),right = nums.length
(右边界,因不包含最后一个元素)。 - 循环条件:
left < right
(当left == right
时,区间为空,无需继续查找)。 - 指针移动:
- 若
nums[mid] > target
:目标在左区间,缩小右边界为right = mid
(右开区间,mid 不包含在新区间内)。 - 若
nums[mid] < target
:目标在右区间,缩小左边界为left = mid + 1
(左闭区间,mid 已排除)。 - 若
nums[mid] == target
:直接返回 mid(找到目标)。
- 若
- 未找到目标:循环结束时
left == right
,此时left
(或right
)即为插入位置。
Java 代码
class Solution {public int searchInsert(int[] nums, int target) {int left = 0; // 左边界int right = nums.length; // 右边界(左闭右开,不包含最后一个元素)// 循环条件:left < right(区间为空时停止)while (left < right) {// 计算 mid:避免溢出int mid = left + (right - left) / 2;if (nums[mid] > target) {// 目标在左区间,右边界设为 mid(右开区间,mid 不包含)right = mid;} else if (nums[mid] < target) {// 目标在右区间,左边界设为 mid + 1(mid 已排除)left = mid + 1;} else {// 找到目标,返回索引return mid;}}// 未找到目标:left == right,即为插入位置return left; // 也可返回 right(两者相等)}
}
复杂度分析
- 时间复杂度:O(log n),二分查找每次将区间缩小一半,最多执行 log₂n 次。
- 空间复杂度:O(1),仅用常量级变量(left、right、mid),无额外空间消耗。