热题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) 时间内解决问题。你的代码虽然与标准写法略有差异,但本质是等价的,且时间复杂度相同。

相关推荐
未若君雅裁2 分钟前
算法复杂度与数据结构:Java 集合篇的第一块基石
java·数据结构·算法
春日见32 分钟前
五分钟入门 强化学习---Q-Learning算法与实现
人工智能·python·深度学习·算法·机器学习·计算机视觉
Zldaisy3d1 小时前
全球唯一仿真驱动自适应扫描路径新版本发布,金属3D打印工艺开发进入算法时代
算法·3d
小江的记录本1 小时前
【JVM虚拟机】类加载机制:类加载全流程:加载→验证→准备→解析→初始化(附《思维导图》+《面试高频考点清单》)
java·jvm·spring boot·算法·安全·spring·面试
故事和你912 小时前
洛谷-【动态规划2】线性状态动态规划4
开发语言·数据结构·c++·算法·动态规划·图论
不吃土豆的马铃薯2 小时前
Socket 网络编程实战教程
linux·服务器·开发语言·网络·c++·算法
weixin_468466853 小时前
图像滤波算法新手实战指南
图像处理·人工智能·算法·计算机视觉·ai·机器视觉·滤波
Ulyanov3 小时前
深入QML-Python通信 构建响应式交互界面的桥梁设计:QML+PySide6现代开发入门(五)
开发语言·python·算法·交互·qml·系统仿真
重生之我是Java开发战士3 小时前
【贪心算法】加油站,单调递增的数字,坏了的计算器,合并区间,用最少数量的箭引爆气球
算法·贪心算法
小欣加油3 小时前
leetcode 3300 替换为数位和后的最小元素
数据结构·c++·算法·leetcode