题目描述

✅ LCR 068. 搜索插入位置

image-20260929011341848

image-20260929011341850

题意分析

在升序且无重复元素的数组中查找目标值:存在时返回它的下标,不存在时返回插入后仍保持升序的位置。

两种情况可以统一为寻找第一个大于等于 target 的位置;如果所有元素都小于它,答案就是数组长度 n,表示追加到末尾。排序保证判定 nums[i] >= target 从假到真最多变化一次,可以用二分定位这个分界。

解法:二分第一个不小于目标的位置

核心思路

[!blue]

初始化 left = 0、right = n。始终保持:下标小于 left 的元素都小于目标,下标大于等于 right 的实际元素都大于等于目标;还没确定类别的元素位于半开区间 [left, right)。

与此同时,待返回的分界位置位于闭区间 [left, right]。答案可以等于右边界,即使 right = n 不是可访问的数组下标,它仍是合法的插入位置。应区分“需要继续比较的元素范围”和“分界可能所在的位置”。

当 left < right 时取中点 mid,有 left <= mid < right <= n,所以 nums[mid] 一定可以访问:

  • 若 nums[mid] >= target,有序性保证从 mid 往右的元素都符合条件,最早位置不会在其右侧。令 right = mid,把这一段归入已知的大于等于部分,同时保留 mid 作为可能的答案。
  • 若 nums[mid] < target,从 left 到 mid 都小于目标,不可能成为答案。令 left = mid + 1,把这一段归入已知的小于部分。

两个分支都保持上述分类并缩短待检查区间。最终 left == right 时,半开区间已经为空:左边全部小于目标,右边全部大于等于目标,这个唯一边界就是所求位置。

目标小于所有元素时,边界会收敛到 $0$;目标大于所有元素时会收敛到 n。返回边界即可,不应再读取 nums[left],因为 left = n 时并没有对应元素。

解题步骤

  1. 令 left = 0、right = nums.length,将末尾插入位置也纳入可能答案。
  2. 在 left < right 时取中点 mid。
  3. 若 nums[mid] >= target,令 right = mid;否则令 left = mid + 1。
  4. 待检查区间为空时,返回 left。

代码实现

class Solution {
    public int searchInsert(int[] nums, int target) {
        int left = 0;
        int right = nums.length;

        while (left < right) {
            int mid = (left + right) >>> 1;

            if (nums[mid] >= target) {
                right = mid;
            } else {
                left = mid + 1;
            }
        }

        return left;
    }
}
func searchInsert(nums []int, target int) int {
    left, right := 0, len(nums)
    for left < right {
        mid := (left + right) >> 1
        if nums[mid] >= target {
            right = mid
        } else {
            left = mid + 1
        }
    }
    return left
}

复杂度分析

  • 时间复杂度:$O(\log(n+1))$,每轮将待检查区间缩小约一半,直到没有待比较元素。
  • 空间复杂度:$O(1)$,只使用左右边界和中点。

关键点总结

[!green]

  • 目标存在与不存在都对应第一个不小于目标的分界位置。
  • 元素待查区间为 [left, right),分界答案的候选范围为 [left, right],二者不能混写。
  • 满足条件的 mid 保留为右边界,不满足的 mid 连同左侧排除。
  • n 是合法返回值,但不是可读取的元素下标。

易错点总结

[!yellow]

  • 当前模板把 right 初始化为 n - 1,会丢掉返回 n 的末尾插入情况。
  • 满足条件时写成 right = mid - 1,可能越过恰好就是答案的 mid。
  • 不满足条件时写成 left = mid,在只剩一个待查元素时无法推进。
  • 返回前再读取 nums[left],会在目标大于所有元素时越界。

相似题目

题目 难度 关联与区别
34. 在排序数组中查找元素的第一个和最后一个位置 中等 同样查找有序数组的边界,本题只找第一个不小于目标的位置,原题还需找到最后一次出现。
704. 二分查找 简单 原题只判断目标是否存在,本题不存在时也要返回唯一插入位置。
转载与许可
作者
链接 https://hgnulb.github.io/blog/2026/15547755
许可 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处!