【算法实战之力扣刷题】搜索插入位置

114 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第27天,点击查看活动详情

题目描述:

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

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

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

输入: nums = [1,3,5,6], target = 2
输出: 1 提示: nums 为 无重复元素 的 升序 排列数组

解题思路:

二分查找法

  • 从数组中间的元素开始,如果中间的元素正好是目标值,搜索结束
  • 如果目标值大于或小于中间的元素,则在大于或小于中间的元素的那一半继续搜索

注:数组为有序数组(升序),并且无重复元素。因为有重复元素,使用二分查找法返回的元素下标可能不是唯一的

特殊判断: 比较目标元素和数组中的最后一个元素,如果大于,返回数组中的最后一个元素的下标加1(目标元素插入的位置)。右侧下标right 设置成数组长度,一开始就不需要特殊判断了。

  1. 定义左侧下标left为0,右侧下标right为nums.length, 在数组该区间里查找第一个大于等于target的元素下标 ; 超出区间,直接返回左侧下标left

  2. 数组中间值 和 目标元素 进行大小判断,nums[mid] < target 则 left 右移(mid+1),nums[mid] >= target 则 right 移到中间位置(mid)

  3. while (left < right) 表示当 left 与 right 重合的时候,搜索终止。退出循环的时候,必然满足 left == right,因此返回 left 或者 right 都可以。

var searchInsert = function(nums, target) {
    const len = nums.length;
    // 特殊判断  如果目标元素严格大于数组中的最后一个元素,将返回数据的最后一个元素的下标加一(也就是数组长度)
    // if(target > nums[len - 1]){
    //   return len;
    // }
    // target <= nums[len - 1],插入位置在区间[0, len-1]
    let left = 0;
    let right = len;
    // 在区间nums[left,right)里查找第一个大于等于target的元素下标  
    while(left < right){
      //中间值  
      let mid = Math.floor((left + right) / 2); 
      // 情况1 如果中间位置的数值严格小于目标值(target),那么mid以及mid左边的就一定不是插入元素位置,下一轮搜索区间[mid+1,right] left = mid+1
      if(nums[mid] < target){
        left = mid+1;
      } else{
        // 情况2 如果mid看到的数值大于等于target 那么mid可能是插入元素的位置,mid的右边一定不存在插入元素的位置,下一轮搜索区间[left,mid] right = mid 
        right = mid;
      }
    }
    return left
};

知识点:时间复杂度

一般用“大O表示法”来表示时间复杂度:T(n) = O(f(n))

T(n): 表示代码的执行时间,
f(n): 表示代码的执行总次数,
n: 表示数据规模的大小

总体理解为代码的执行时间和代码的执行次数成正比,代码的执行时间随数据规模增长的变化趋势。

常见的时间复杂度量级

常数阶O(1)

代码执行时消耗的时间不受某个变量 (n) 的增长而影响,这样的代码复杂度就为 O(1)。

const a = n;
const b = 2 * n;
return a + b;

注:一般情况下除了循环语句、递归语句,时间复杂度都为 O(1)

线性阶O(n)

代码执行时消耗的时间随着变量(n)的变化而变化,这类代码都可以用O(n)来表示它的时间复杂度。

const n = 100;
  for (let i = 0; i < n; i++) {
      let sum = 0;
      sum += i;
      return sum;
  }

对数阶O(logn)

时间复杂度:0(logn),其中n为数组的长度。二分查找所需的时间复杂度为0(logn)

let i = 1;
const n = 8;
while (i < n) {
  i = i * 2;
}

i的变化规律: 2^0, 2^1, 2^2, 2^3

当循环3次后退出,也就是说2^3 = n。那么 3 = log2^n,得出时间复杂度为 O(logn)。二分查找的时间复杂度就是 O(logn)。