35. 搜索插入位置
链接:https://leetcode-cn.com/problems/search-insert-position/
题目描述见链接内容。
解法1:暴力解法
因为数组是有序的,直接暴力遍历就好,找到第一个大于等于target
的元素,返回下标即可,如果没有找到,就返回数组长度
var searchInsert = function (nums, target) {
const length = nums.length;
for (let i = 0; i < length; i++) {
if (nums[i] >= target) {
return i;
}
}
return length;
};
- 时间复杂度:
${O(N)}$
- 空间复杂度:
${O(1)}$
- 执行结果:耗时76ms,击败了JavaScript的92%用户,内存消耗38.6MB,击败了84%的用户
解法2:二分法
暴力解法虽然爽,但是题目的两个特点:1数组有序;2查找某个元素,如果算法感觉敏感的话,应该立刻想到二分法,结果看了题解才想到二分法,而且自己还绊绊磕磕写不出来,正好这道题目的评论下面有人总结了二分法的笔记,趁这个机会单独学习、练习一下二分法
回到这道题目,我们要找到的其实就是第一个大于等于target
的下标,这个下标的成立条件是:
nums[pos- 1] < target <= nums[pos]
所以在发现midVal
大于target
是,就更新result
值,直到循环结束,result
就是满足上述条件的下标
var searchInsert = function (nums, target) {
const length = nums.length;
let min = 0,
max = length - 1,
result = length;
while (min <= max) {
const midIndex = ~~((min + max) / 2);
const midVal = nums[midIndex];
if (midVal === target) {
return midIndex;
}
if (midVal > target) {
max = midIndex - 1;
result = midIndex;
} else {
min = midIndex + 1;
}
}
return result;
};
- 时间复杂度:
${O(log n)}$
- 空间复杂度:
${O(1)}$
- 执行结果:耗时84ms,击败了JavaScript的63.90%用户,内存消耗38.9MB,击败了30.52%的用户
解法3:二分法优化
看了题解的评论,发现上面的解法可以优化,实际上result
是不需要的,直接返回min
就可以
因为根据if
的判断条件,left
左边的值都小于target
,right
右边的值都大于target
,而且left
最终一定等于right+1
,循环结束后,在left
和right
之间画一条竖线,把数组分为两个部分,left
左边全小于target
,right
右边全都大于target
,并且由于遍历结束后left
等于right+1
,所以right
右边的部分,并且以left
为首,所以答案是left
为什么
left
一定等于right+1
,因为每次循环至少会把[left, right]
区间长度减少1
,当l === r
时,mid === l === r
,如果target > nums[l]
,l
就会向右移动一位l = mid + 1
,如果target <= nums[l]
,那么r
就会向左移动一位r = mid - 1
,结果一样因为
while
的判断条件是l <= r
,如果l
不大于r
会是死循环
var searchInsert = function (nums, target) {
const length = nums.length;
let min = 0,
max = length - 1;
while (min <= max) {
const midIndex = ~~((min + max) / 2);
const midVal = nums[midIndex];
if (midVal === target) {
return midIndex;
}
if (midVal > target) {
max = midIndex - 1;
} else {
min = midIndex + 1;
}
}
return min;
};
- 时间复杂度:
${O(log n)}$
- 空间复杂度:
${O(1)}$
- 执行结果:耗时84ms,击败了JavaScript的80.98%用户,内存消耗38.9MB,击败了30.52%的用户
感想
看了一个人的评论,二分法思想很简单,但是细节是魔鬼,确实感觉如此,大致思路知道,但是边界条件仔细思考起来一堆的问题,确实需要针对性的练习巩固一下二分法