热题100 - 35. 搜索插入位置

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

请必须使用时间复杂度为 O(log n) 的算法。

示例 1:

ini 复制代码
输入: nums = [1,3,5,6], target = 5
输出: 2

示例 2:

ini 复制代码
输入: nums = [1,3,5,6], target = 2
输出: 1

示例 3:

ini 复制代码
输入: nums = [1,3,5,6], target = 7
输出: 4

提示:

  • 1 <= nums.length <= 104
  • -104 <= nums[i] <= 104
  • nums无重复元素升序 排列数组
  • -104 <= target <= 104

思路:

这道题是个简单题目。logn是典型的二分查找复杂度。直接给出实现了。

Java 复制代码
class Solution {
    public int searchInsert(int[] nums, int target) {
        int i = 0, n = nums.length;
        while (i < n) {
            int mid = (i + n) >> 1;
            if (i == mid) {
                return target <= nums[i] ? i : i+1;
            }
            if (nums[mid] > target) {
                n = mid;
            } else if (nums[mid] < target) {
                i = mid;
            } else {
                return mid;
            }
        }
        return 0;
    }
}

这段代码实现了正确的搜索插入位置功能,但在二分法的实现上与标准写法有所不同。以下是关键点分析:

  1. 区间处理 :代码使用左闭右开区间 [i, n),通过调整 in 缩小区间。例如,nums[mid] < target 时,i = mid,相当于新的区间为 [mid, n),这可能导致缩小区间较慢,但通过循环条件和提前返回仍能正确收敛。
  2. 终止条件 :在 i == mid 时(即区间长度接近1时),直接判断插入位置。这处理了剩余元素的边界情况,避免了无限循环,确保了正确性。
  3. 循环外返回:当数组为空时,直接返回0,符合题目要求。其他情况循环内均会返回,不会执行到此处。

虽然代码与常见的左闭右闭或左闭右开的标准二分法有所区别(如调整区间时不使用 mid ± 1),但其逻辑正确,能够覆盖所有情况。因此,可以视为一种有效的二分法变种。

结论:该实现是正确且高效的二分法,尽管写法与某些标准形式不同,但核心思想一致。

讲讲标准的二分查找

标准的二分法实现通常有两种常见写法,以下是针对「搜索插入位置」问题的两种标准实现,并附详细解释:


标准写法 1:左闭右闭区间 [left, right]

java 复制代码
class Solution {
    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; // 目标在左半部分
            }
        }
        // 循环结束时,left 是第一个大于 target 的位置
        return left;
    }
}
关键点解释:
  1. 区间定义

    使用左闭右闭区间 [left, right],即搜索范围始终包含 leftright 指向的元素。

  2. 循环条件
    while (left <= right) 保证区间合法时继续搜索。当 left > right 时,所有元素已检查完毕。

  3. 区间更新

    • 如果 nums[mid] < target,说明目标在右半部分,更新 left = mid + 1
    • 如果 nums[mid] > target,说明目标在左半部分,更新 right = mid - 1
  4. 返回值

    循环结束时,left 指向第一个大于 target 的位置,即插入位置。若数组为空,直接返回 0


标准写法 2:左闭右开区间 [left, right)

java 复制代码
class Solution {
    public int searchInsert(int[] nums, int target) {
        int left = 0, right = nums.length;
        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; // 目标在左半部分(右开)
            }
        }
        // 循环结束时,left 和 right 重合,即插入位置
        return left;
    }
}
关键点解释:
  1. 区间定义

    使用左闭右开区间 [left, right),即搜索范围包含 left 指向的元素,但不包含 right

  2. 循环条件
    while (left < right) 保证区间合法时继续搜索。当 left == right 时,区间为空。

  3. 区间更新

    • 如果 nums[mid] < target,更新 left = mid + 1

    • 如果 nums[mid] > target,更新 right = mid(因为右开,不包含 mid)。

  4. 返回值

    循环结束时,leftright 重合,指向插入位置。


用户代码与标准写法的对比

你的代码逻辑是正确的,但有以下特点:

  1. 区间更新方式

    你使用了类似左闭右开的方式(in 的更新),但没有显式区分区间定义,而是通过提前判断 i == mid 处理边界条件。

  2. 提前返回

    当区间缩小到长度为 1 时(i == mid),直接比较 targetnums[i] 并返回结果。这避免了标准写法中循环结束后再判断的步骤。

  3. 代码简洁性

    标准写法更注重统一处理所有情况,而你的代码通过提前返回简化了部分逻辑。


总结

两种标准写法都严格遵循二分法的核心思想:通过不断折半缩小搜索范围 。它们的区别仅在于区间定义和边界处理,但最终都能在 O(log n) 时间内解决问题。你的代码虽然与标准写法略有差异,但本质是等价的,且时间复杂度相同。

相关推荐
快来卷java8 分钟前
深入剖析雪花算法:分布式ID生成的核心方案
java·数据库·redis·分布式·算法·缓存·dreamweaver
阿巴~阿巴~10 分钟前
C/C++蓝桥杯算法真题打卡(Day11)
算法
tpoog14 分钟前
[MySQL]数据类型
android·开发语言·数据库·mysql·算法·adb·贪心算法
刚入门的大一新生34 分钟前
排序算法3-交换排序
算法·排序算法
董董灿是个攻城狮2 小时前
Transformer 通关秘籍7:词向量的通俗理解
算法
卷卷的小趴菜学编程2 小时前
算法篇-------------双指针法
c语言·开发语言·c++·vscode·算法·leetcode·双指针法
komo莫莫da2 小时前
Day14 动态规划(3)
算法·深度优先·动态规划
地平线开发者3 小时前
【征程 6】工具链 VP 示例为什么能运行
算法·自动驾驶
ElseWhereR3 小时前
困于环中的机器人
c++·算法·leetcode
学也不会3 小时前
d2025331
java·数据结构·算法