前期知识点
文档链接:数组理论基础(代码随想录)
704.二分查找
题目链接:力扣
文档链接:代码随想录
视频讲解:b站
自己看到题目的第一想法
使用for循环遍历数组,与target值进行对比,若有某个值与target相等,则返回该元素的下标。
看完代码随想录之后的想法(思路)
该题的前提是有序数组,且无重复元素(因为如果数组存在重复元素,使用二分法返回的元素下标不唯一),这都是使用二分法的前提条件,当题目看到这两个关键字,应该联想到使用二分法。
二分法查找的难点在于处理边界问题:
- 到底是while( left< right) 还是 while(left <= right ) ;
- 到底是right=middle 呢,还是 right = middle-1;
解决该问题就是要坚持循环不变量原则:大家在写二分法经常写乱的原因就是对区间的定义没有想清楚,区间的定义就是不变量 。(我们每次在处理边界问题时,都要坚持区间的定义来操作)
写二分法时,其区间的定义一般分为两种:左闭右闭[left,right] 或者 左闭右开[left ,right)
写法一
我们定义target在一个左闭右闭的区间里,即[left,right] ,那么边界处理问题如下:
- 应该使用while(left <= right),因为 left = right 是有意义的,所以可以取等号;
- if(nums[middle] > target) ,那么right 的值就要赋值为 middle-1,因为当前这个nums[middle]一定不是target,所以接下来要查找的左区间结束下标位置就是 middle - 1
代码如下:
var search = function(nums, target) {
let middle, left=0
let right=nums.length-1
while(left<=right){
//位运算 + 防止大数溢出 (等同于 (left + right) /2 )
middle=left + ((right - left) >> 1);
if(nums[middle]>target){
right=middle-1
}else if(nums[middle]<target){
left=middle+1
}else{
return middle
}
}
return -1
};
- 时间复杂度:O(log n)
- 空间复杂度:O(1)
写法二
我们定义target在一个左闭右开的区间里,即[left,right) ,其边界处理问题如下:
- 应该使用while(left < right),因为 left == right 在区间[left,right)是没有意义的;
- if(nums[middle] > target) ,那么right 的值就要赋值为 middle,因为寻找的区间是左闭右开区间,在下一个查询区间不会去比较nums[middle]
代码如下:
var search = function(nums, target) {
let middle, left=0
let right=nums.length
while(left<right){
//位运算 + 防止大数溢出 (等同于 (left + right) /2 )
middle=left + ((right - left) >> 1);
if(nums[middle]>target){
right=middle
}else if(nums[middle]<target){
left=middle+1
}else{
return middle
}
}
return -1
};
- 时间复杂度:O(log n)
- 空间复杂度:O(1)
总结
对于二分法经常会出错的主要原因就是,对区间的定义没有理解清楚,我们在循环中应该始终坚持查找区间的定义来做边界处理.区间的定义就是不变量,那么循环中坚持查找区间的定义来做边界处理,就是循环不变量原则.
27.移除元素
题目链接:力扣
文档链接:代码随想录
视频讲解:b站
看完代码随想录之后的想法(思路)
前言
解释一下题目原文 “你不需要考虑数组中超出新长度后面的元素。”及示例中正确答案本来是nums[2,2]而nums[2,2,3,3] 或nums[2,2,0,0]也会被视为正确答案的原因。
因为数组在内存地址中是连续分布的,不能单独删除数组中某个元素,只能覆盖。所以,多余的数组无法直接删掉,还保留在原数组中。
双指针法(快慢指针法)
解释:通过一个快指针和一个慢指针在一个for循环下完成两个for循环的工作。
定义:
- 快指针:寻找新数组的元素,新数组就是不包含等于val值的元素。
- 慢指针:指向更新后 新数组的下标。
代码如下
/**
* @param {number[]} nums
* @param {number} val
* @return {number}
*/
var removeElement = function(nums, val) {
let slow=0
for(fast=0;fast<nums.length;fast++){
if(nums[fast]!=val){
nums[slow]= nums[fast]
slow++
}
}
return slow
};
- 时间复杂度:O(n)
- 空间复杂度:O(1)
总结
双指针法在数组和链表的操作中是非常常见的,很多考察数组、链表、字符串等操作的面试题,都使用双指针法。