题目描述:
给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。
你可以假设数组中无重复元素。
示例一
输入: [1,3,5,6], 5
输出: 2
示例二
输入: [1,3,5,6], 2
输出: 1
示例三
输入: [1,3,5,6], 7
输出: 4
示例三
输入: [1,3,5,6], 0
输出: 0
思路分析
考虑这个插入的位置 ,它成立的条件为:
其中 代表排序数组。由于如果存在这个目标值,我们返回的索引也是 ,因此我们可以将两个条件合并得出最后的目标:「在一个有序数组中找第一个大于等于 的下标」。
但是这题还要注意边界情况
- 目标值在数组所有元素之前
- 目标值等于数组中某一个元素
- 目标值插入数组中的位置
- 目标值在数组所有元素之后
这四种情况确认清楚了,就可以尝试解题了。
暴力法
这个就不多说了,直接上代码。
AC代码
fun searchInsert(nums: IntArray, target: Int): Int {
for (i in 0 until nums.size) {
// 分别处理如下三种情况
// 目标值在数组所有元素之前
// 目标值等于数组中某一个元素
// 目标值插入数组中的位置
if (nums[i] >= target) {
// 一旦发现大于或者等于target的num[i],那么i就是我们要的结果
return i
}
}
// 目标值在数组所有元素之后的情况
return nums.size //如果target是最大的,或者 nums为空,则返回nums的长度
}
复杂度分析
时间复杂度:
空间复杂度:
二分查找
要注意这道题目的前提是数组是有序数组,这也是使用 二分查找 的基础条件。
以后只要看到题里给出的数组是有序数组,都可以想一想是否可以使用二分法。
同时题目还强调数组中无重复元素,因为一旦有重复元素,使用二分查找法返回的元素下表可能不是唯一的。
本题整体思路和普通的二分查找几乎没有区别,
- 先设定左侧下标
left和右侧下标right,再计算中间下标mid - 每次根据
nums[mid]和target之间的大小进行判断,相等则直接返回下标,nums[mid]<target则left右移,nums[mid] > target则right左移 - 查找结束如果没有相等值则返回
left,该值为插入位置
AC代码
class Solution {
fun searchInsert(nums: IntArray, target: Int): Int {
var left =0
var right = nums.size - 1 // 定义target在左闭右闭的区间里,[left, right]
while (left <= right) { // 当left==right,区间[left, right]依然有效
var mid = (left + right) / 2 // 防止溢出 等同于(left + right)/2
if (nums[mid] == target) {
return mid // target 在左区间,所以[left, middle - 1]
} else if (nums[mid] < target ) {
left = mid +1 // target 在右区间,所以[middle + 1, right]
} else { // nums[middle] == target
right = mid +1
}
}
// 分别处理如下四种情况
// 目标值在数组所有元素之前 [0, -1]
// 目标值等于数组中某一个元素 return middle;
// 目标值插入数组中的位置 [left, right],return right + 1
// 目标值在数组所有元素之后的情况 [left, right], return right + 1
return left
}
}
复杂度分析
时间复杂度:
空间复杂度:
参考
总结
这个说实话我一开始只想到了暴力解法,毕竟也才入门,算法基础这两天边刷边补。 然后看了题解,尝试使用了二分法
然后理论上来说
暴力解法 时间复杂度:
二分法解法 时间复杂度:
但那都是理论上的,世事无绝对,暴力解题 不一定时间消耗就非常高,关键看实现的方式,就像是二分查找时间消耗不一定就很低,是一样的。
我用二分法提交了2次都是 超出时间限制 - -
再接再厉。
本文正在参与「掘金 2021 春招闯关活动」, 点击查看 活动详情