这是我参与2022首次更文挑战的第11天,活动详情查看:2022首次更文挑战
题目描述
给一个排好序的数组,和一个目标值,在数组中找该目标值,如果存在,则返回该索引,否则返回它存在这个数组应该存在的位置的索引。
举个例子:
arr: [2,3,5]
目标值: 5
返回: 2
arr: [2,3,5]
目标值: 2
返回: 0
arr: [2,3,5]
目标值: 4
返回: 2
思路分析
常规遍历
根据已知条件,数组是排好序的,我们可以先判断目标值是不是在数组的范围内。
如果不在,则无需遍历,直接返回了。
所以我们先让它跟数组的第一个和最后一个数比较
- 如果目标值小于第一个数,则直接返回0即可(因为它会插入数组的开头)。
- 如果目标值大于最后一个数,则直接返回数组的长度即可(因为它会插入数组的结尾)。
如果在的话,则需要遍历判断
- 如果遍历的数和目标值相等,则直接返回当前索引。
- 如果目标值比当前遍历的数大,并且小于下一个遍历的数,则证明目标值并不在数组中,返回当前的索引加一即可(因为它会插入当前遍历的数的下一个数)。
代码如下:
/**
* @param {number[]} nums
* @param {number} target
* @return {number}
*/
var searchInsert = function (nums, target) {
if (nums[0] > target) return 0
if (nums[nums.length - 1] < target) return nums.length
for (let i = 0; i < nums.length; i++) {
if (nums[i] === target) return i
if (nums[i] <= target && target <= nums[i + 1]) {
return i + 1
}
}
};
上面的时间复杂度是O(n),
那你能使用时间复杂度为 O(log n) 的算法吗?
可以的,可以使用二分法。
二分法
这个是利用首尾索引当作左右的索引,折中取中间的值,和目标值比较,如果目标值大,则把中间的索引加一赋值给左边,如果目标值小,则把中间的索引减一赋值给右边。然后如果和目标值相等,则直接返回中间的索引。
不相等继续折中取中间值,直到左边索引大于右边才退出while循环,然后返回左边索引,或者中间值等于目标值才返回。
代码如下:
/**
* @param {number[]} nums
* @param {number} target
* @return {number}
*/
var searchInsert = function (nums, target) {
let left = 0;
let right = nums.length - 1
while (left <= right) {
let mid = left + parseInt((right - left) / 2)
const num = nums[mid]
if (target > num) {
left = mid + 1
} else if (target < num) {
right = mid - 1
} else {
return mid
}
}
return left
};