力扣刷题- 搜索插入位置
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] 里查找目标元素。
以上是关于力扣刷题- 搜索插入位置的主要内容,如果未能解决你的问题,请参考以下文章