LeetCode #35 搜索插入位置
给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。 你可以假设数组中无重复元素。
示例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
方法一
从题目意思看,需要把一个元素按大小顺序进行有序插入。 观察示例,把数组记作arr, 目标值为val,插入的位置:index 示例1 存在目标值,得出关系arr[index] === val
示例2 不存在目标值,目标值处于数组中间。得出关系arr[index] > val
示例3 目标值不存在数组且大于数组最大值。得出关系index > arr[arr.length - 1],也即 index === arr.length
示例4 目标值不存在数组且小于数组最小值。得出关系arr[index] > val
上述四种其实就是所有可能的情况,不难发现,插入的位置和目标值 的关系: arr[index] >= val 或者 index === arr.length,所以有一种遍历的解法
/**
* @param {number[]} nums
* @param {number} target
* @return {number}
*/
// 遍历n次,时间复杂度O(n)
var searchInsert = function(nums, target) {
for(var i=0; i<nums.length; i++) {
if(nums[i] >= target) return i;
}
return nums.length;
};
上述意思,遍历数组,一旦当前元素值大于或等于目标值时,
当前位置便是要找的位置,如果数组中没有出现这个值,
说明目标值超过数组最大值,此时返回最大数组长度。
方法二
题目中提到数组是有序的,且目的是插入元素。相当于你找到数组中与目标值大小接近的元素位置,就能知道插在哪了。
// 二分法
var searchInsert = function(nums, target) {
var left = 0, right = nums.length;
while(left <= right) {
var mid = Math.floor((left + right) / 2);
if(nums[mid] === target) {
return mid;
} else if(nums[mid] < target) {
left = mid + 1;
} else if(nums[mid] > target) {
right = mid - 1;
}
}
return left;
};
大致思路:每次取数组中间值和目标值进行比较,
判断目标值处于数组的哪个范围,
并且不断对半切割数组,缩小范围,最终找到目标值的位置。
至于为什么最后返回left,因为要么返回mid,是中间值与目标值恰好相等的情况,
要么跳出循环,条件是left > right,
说明最后一个循环是判断nums[mid] < target,
然后left = mid + 1 超过了right,而target 的位置比mid大,就是mid后一个位置即left
可以辅助画图比较清楚。
LeetCode #27 移除元素
给定一个数组 nums 和一个值 val,你需要原地移除所有数值等于 val 的元素,返回移除后数组的新长度。 不要使用额外的数组空间,你必须在原地修改输入数组并在使用 O(1) 额外空间的条件下完成。 元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素
示例1:给定 nums = [3,2,2,3], val = 3, 函数应该返回新的长度 2, 并且 nums 中的前两个元素均为 2。你不需要考虑数组中超出新长度后面的元素。
示例2:给定 nums = [0,1,2,2,3,0,4,2], val = 2,函数应该返回新的长度 5, 并且 nums 中的前五个元素为 0, 1, 3, 0, 4。注意这五个元素可为任意顺序。你不需要考虑数组中超出新长度后面的元素。
方法一
思路:题目强调了不要使用额外的数组空间,原地修改,移除数组中值为val的元素返回剩余长度。可以想想 js 中移除原数组元素的方法,shift, pop, splice,由于不一定是首尾的元素,能想到的也就是splice,好的
// 时间复杂度O(n * n)
var removeElement = function(nums, val) {
for(var i=0; i<nums.length; i++) {
if(nums[i] === val) {
// 找到相等的元素,`原地`删除
nums.splice(i, 1);
// 注意删除后后面的元素往前移一位,故i不变
i -= 1;
}
}
return nums.length;
};
方法二
/*
双指针法, 一旦找到不为val值的元素,从头开始保存
时间复杂度 O(n)
*/
var removeElement = function(nums, val) {
var point = 0; // 创建指针
for(var i=0; i<nums.length; i++) {
if(nums[i] !== val) {
// 当前元素不用移除
nums[point] = nums[i]; // 从头保存
point++; // 指针后移
}
}
// point的长度便是新数组长度
return point;
}
// 上面可能会有疑问,数组等于val的元素并没有被移除啊。
// 别忘了题目还有个条件:你不需要考虑数组中超出新长度后面的元素
方法三
/*
双指针法
好处是如果数组中需要移除的元素数量较少时,可以减少赋值次数,缩短时间
*/
var removeElement = function(nums, val) {
var start = 0, end = nums.length;
while(start < end) {
if(nums[start] === val) {
// 找到需要移除的值时,用最后一个元素来替换,同时数组缩短1
nums[start] = nums[end - 1];
end -= 1;
} else {
start += 1;
}
}
return end;
}
欢迎评论区交流