LeetCode 刷题笔记【35.搜索插入位置】

Posted by Leonsux on April 19, 2022

题目

https://leetcode-cn.com/problems/search-insert-position

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

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

示例

输入: nums = [1,3,5,6], target = 5

输出: 2

输入: nums = [1,3,5,6], target = 7

输出: 4

常规遍历

首先我们可能会想到遍历数组,找到合适的位置,返回下标。

是没错,这么做很简单。

因为是升序数组,我们自左往右遍历,如果比目标值小或者等于目标值,直接返回当前下标即可;如果遍历结束都未满足条件,那说明目标值比任意一个数组元素都大,所以返回数组长度。

var searchInsert = function(nums, target) {
  for (let i = 0; i < nums.length; i++) {
    if (nums[i] >= target) {
      return i;
    }
  }

  return nums.length;
};

好,提交!通过!但是貌似少点东西,仔细看题目,“请必须使用时间复杂度为 O(log n) 的算法。” 很显然我们直接遍历时间复杂度为O(n),并不符合题目要求。

二分查找

时间复杂度O(log n),那就明示我们使用二分查找了。

二分查找顾名思义体现一个二分,从数组的中间位置将数组分成两部分;

具体实现时需要两个下标(left, right)来框定数组的范围。中间值我们定义为 mid,mid = (left + right) >> 1(首位之和按二进制右移一位相当于除以二向下取整)。

比较中间位置和目标值,如果相等,那直接返回中间下标;

如果中间位置小于目标值,重新框定范围,取数组右半边,left = mid + 1,right 不变;

反之,取数组左半边,right = mid - 1,left 不变;

最后当 left 比 right 大的时候,说明数组中并不存在目标值(如果存在我们上面早就return掉了),而且当前位置肯定是目标值的合适插入位置,那这个插入位应该怎么取呢?是 left 还是 right ?

首先明确一点,当 left 变得比 right ,有两种情况:

1、left = mid + 1,导致 left > right

2、right = mid - 1,导致 right < left

但在这之前,left 和 right是相等的,他们之和除以2也和他们相等,这时候比较中间值和目标值,如果目标值大,那目标值插入的位置要靠后一位(+1),于此同时按照上面的逻辑我们的left+1;否则就在当前位置插入即可,此时我们的left不变。

所以最终结论是:直接返回 left 即可

var searchInsert = function(nums, target) {
  let left = 0;
  let right = nums.length - 1;

  while(left <= right) {
    let mid = (left + right)>>1;
    if (nums[mid] === target) {
      return mid;
    }
    if (nums[mid] < target) {
      left = mid + 1;
    } else {
      right = mid - 1;
    }
  }

  return left;
};