力扣刷题- 搜索插入位置

Posted 王六六的IT日常

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了力扣刷题- 搜索插入位置相关的知识,希望对你有一定的参考价值。

力扣题目链接

题目描述

给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。
如果目标值不存在于数组中,返回它将会被按顺序插入的位置。
使用时间复杂度为 O(log n) 的算法。

你可以假设数组中无重复元素。

示例 1: 输入: [1,3,5,6], 5 输出: 2

示例 2: 输入: [1,3,5,6], 2 输出: 1

示例 3: 输入: [1,3,5,6], 7 输出: 4

示例 4: 输入: [1,3,5,6], 0 输出: 0

提示:nums 为无重复元素的升序排列数组 - 最后一个值肯定是最大的

思路

有序数组中查找,可以使用「二分查找」。

  • 要在数组中插入目标值,无非是这四种情况:

    • 目标值在数组所有元素之前
    • 目标值等于数组中某一个元素
    • 目标值插入数组中的位置
    • 目标值在数组所有元素之后
  • 整体思路和普通的二分查找几乎没有区别,先设定左侧下标 left 和右侧下标 right,再计算中间下标 mid

  • 每次根据 nums[mid] 和 target 之间的大小进行判断,相等则直接返回下标,nums[mid] < target 则 left 右移,nums[mid] > target 则 right 左移

  • 查找结束如果没有相等值则返回 (left)第 1 个 大于等于(等于的情况可以看示例 1) 目标元素的下标,该值为插入位置

  • 如果当前mid值严格小于 target,那么 mid 以及 mid 左边的所有元素就一定不是题目要求的结果

  • 时间复杂度: O ( l o g n ) O(logn) O(logn)

参考代码一:

public class Solution {

    public int searchInsert(int[] nums, int target) {
        int len = nums.length; 
        // 特殊判断
        if (nums[len - 1] < target) {
            return len;
        }

        // 程序走到这里一定有 nums[len - 1] >= target
        int left = 0;
        int right = len - 1; //【左闭右闭】
        // 在区间 nums[left..right] 里查找第 1 个大于等于 target 的元素的下标
        // while(left < right){
        //     int mid = (left + right) / 2;
        //     if(nums[mid] == target){
        //         return mid;
        //     }else if(nums[mid] < target){
        //         left = mid +1;
        //     }else if(nums[mid] > target){
        //         right = mid;
        //     }
        // }
        while (left < right) {
            int mid = (right + left) / 2;
            if (nums[mid] < target){
                // 下一轮搜索的区间是 [mid + 1..right]
                left = mid + 1;
            } else { //nums[mid] >= target
                // 下一轮搜索的区间是 [left..mid]
                right = mid;
            }
        }
        return left;
    }
}

既然 len 也有可能是答案,可以在初始化的时候,把 right 设置成 len,此时就不需要特殊判断了。

参考代码二:

public class Solution {

    public int searchInsert(int[] nums, int target) {
        int len = nums.length;
        int left = 0;
        int right = len;
        // 在区间 nums[left..right] 里查找第 1 个大于等于 target 的元素的下标
        while (left < right) {
            int mid = left + (right - left) / 2;
            if (nums[mid] < target){
                // 下一轮搜索的区间是 [mid + 1..right]
                left = mid + 1;
            } else {
                // 下一轮搜索的区间是 [left..mid]
                right = mid;
            }
        }
        return left;
    }
}

注意点概括

  • 写成 while(left < right) ,退出循环的时候有 left == right 成立,好处是不用判断应该返回 left 还是 right;
  • 区间 [left…right] 划分只有以下两种情况:
    • 分成 [left…mid] 和 [mid + 1…right],分别对应 right = mid 和 left = mid + 1;int mid = (left + right) / 2
    • 分成 [left…mid - 1] 和 [mid…right],分别对应 right = mid - 1 和 left = mid,这种情况下。需要将 int mid = (left + right) / 2 改成 int mid = (left + right + 1) / 2,否则会出现死循环,这一点不用记,出现死循环的时候,把 left 和 right 的值打印出来看一下就很清楚了;
  • 退出循环 left == right,如果可以确定区间 [left…right] 一定有解,直接返回 left 就可以。否则还需要对 left 这个位置单独做一次判断;
  • 二分查找的循环不变量是:在区间 [left…right] 里查找目标元素。

以上是关于力扣刷题- 搜索插入位置的主要内容,如果未能解决你的问题,请参考以下文章

2.LeetCode刷题-搜索插入位置

2.LeetCode刷题-搜索插入位置

力扣刷题:单词搜索(java实现)

力扣刷题:单词搜索(java实现)

力扣刷题:二叉搜索树中第K小的元素(java实现)

力扣刷题:二叉搜索树中第K小的元素(java实现)