leetcode
leetcode 1 ~ 50
搜索插入位置

搜索插入位置

难度:

标签:

题目描述

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

请必须使用时间复杂度为 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

 

提示:

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

代码结果

运行时间: 32 ms, 内存: 15.4 MB


// Java Stream Solution
// Problem: Same as above.
// Approach: Use Java Streams to find the index or insertion point. Convert the array to a Stream and use binary search.
 
import java.util.Arrays;
 
public class Solution {
    public int searchInsert(int[] nums, int target) {
        return Arrays.binarySearch(nums, target) >= 0 ? Arrays.binarySearch(nums, target) : -Arrays.binarySearch(nums, target) - 1;
    }
}

解释

方法:

这个题解使用了二分查找的思路。首先定义左右边界 l 和 r,分别指向数组的开始和结束位置。然后使用 while 循环进行二分查找,每次循环都计算中点 mid,比较 nums[mid] 和 target 的大小关系。如果 nums[mid] >= target,说明目标值可能在左半部分,将右边界 r 移动到 mid;否则目标值在右半部分,将左边界 l 移动到 mid + 1。最终左右边界重合时,l 即为目标值应该插入的位置。

时间复杂度:

O(log n)

空间复杂度:

O(1)

代码细节讲解

🦆
为什么在比较nums[mid]与target时选择将r移动到mid而不是mid-1,这样的选择对算法的效率和结果有什么影响?
在二分查找中,将r移动到mid而不是mid-1是为了确保不错过目标值target。如果nums[mid]等于target,那么mid就是一个可能的插入位置。将r设为mid而不是mid-1,可以保持搜索范围内仍包含当前找到的target的位置,从而确保能找到最左边的插入点。如果将r设为mid-1,则可能会错过正确的插入位置,尤其是当数组中存在多个相同的元素时。这种选择在效率上可能会导致额外的几次迭代,但在结果上更能确保正确性。
🦆
在while循环的条件为l < r而不是l <= r,这样设计的原因是什么?它如何确保不会错过目标值?
使用l < r作为循环条件的主要原因是避免在搜索空间为空时还执行循环体。在l等于r时,搜索空间为空,因此没有必要再继续检查。这样设计可以防止无限循环和访问数组越界。同时,因为算法在每次迭代时都精确调整l或r,所以当l等于r时,l指向的位置就是target应该插入的位置,从而确保不会错过目标值。
🦆
如果数组中的元素非常接近或达到了整型的极限值,比如10^4或-10^4,这会对二分查找算法的效果有什么潜在影响?
二分查找算法计算中点位置时使用的是mid = l + (r - l) / 2公式,这种方式可以防止在计算mid时可能发生的整数溢出(如果直接使用(l + r) / 2可能会溢出)。因此,即使数组中的元素值非常大或接近整型的极限值,只要不超过整型的表示范围,这种计算方法仍然有效,不会影响算法的正确性。
🦆
二分查找的终止条件是l与r重合,即l == r时循环结束。为什么这样的设计能确保找到正确的插入位置?
在二分查找中,每次迭代都是在缩小搜索范围,直到l与r重合。此时,l(或r)指向的位置正是target可以插入的正确位置。这是因为每次循环都是根据nums[mid]与target的比较来调整l或r,确保不会错过target应该存在的位置。当l与r重合时,这个位置就是最终确定的插入位置,无论target是否存在于数组中。

相关问题

第一个错误的版本

你是产品经理,目前正在带领一个团队开发新的产品。不幸的是,你的产品的最新版本没有通过质量检测。由于每个版本都是基于之前的版本开发的,所以错误的版本之后的所有版本都是错的。

假设你有 n 个版本 [1, 2, ..., n],你想找出导致之后所有版本出错的第一个错误的版本。

你可以通过调用 bool isBadVersion(version) 接口来判断版本号 version 是否在单元测试中出错。实现一个函数来查找第一个错误的版本。你应该尽量减少对调用 API 的次数。

 

示例 1:

输入:n = 5, bad = 4
输出:4
解释:
调用 isBadVersion(3) -> false 
调用 isBadVersion(5) -> true 
调用 isBadVersion(4) -> true
所以,4 是第一个错误的版本。

示例 2:

输入:n = 1, bad = 1
输出:1

 

提示:

  • 1 <= bad <= n <= 231 - 1