LeetCode704二分查找
题目描述:给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1。
解题思路
- 这道题其实重点在于对二分法的要点掌握,二分法通过取中间值来进行比较,但是解题思路重点应该放在不断分割的区间,而不是中间值。
- 要理解区间的含义,区间代表当前数组所要进行查询的范围,通常是左闭右闭、左闭右开两种。
- 写的时候要时刻注意保持上下区间格式一致。
解题方法及注意事项
方法一 | 左闭右闭
let start = 0;
let end = nums.length - 1;
while(start <= end){
let mid = Math.floor((start + end) / 2);
console.log(start,end,mid)
if(nums[mid] > target){
end = mid - 1;
} else if(nums[mid] < target){
start = mid + 1;
} else {
return mid;
}
}
return -1;
注意点
- 左闭右闭则代表该区间的两端都是要进行查找的数据,因此end的初始值在这应该是
nums.length - 1。 - while循环判断语句中,要用
<=,因为对于左闭右闭区间来说,[x,x]是合法的区间存在。 - 在if判断中,如果中间值大于目标值,end应更改为
mid - 1,原因是mid在此时已经和target进行了比较,因此再下一轮比较中不再需要查找,同理start的变化应为mid + 1。 - 对于mid的取值,此处如果是
(start + end)/2则会有浮点数出现,因此采用了Math.floor方法进行向下取整。
方法二 | 左闭右开
let start = 0;
let end = nums.length;
while(start < end){
let mid = Math.floor((start + end) / 2);
if(nums[mid] > target){
end = mid;
} else if(nums[mid] < target) {
start = mid + 1;
} else{
return mid;
}
}
return -1;
注意点
- 在写完方法一后写这种方法时非常容易出错的地方在对end初始值的设置,此处的end是开区间,因此是不包括在内的,因此应该为
nums.length - 循环判断以及start、end的写法思路与方法一一致。
总结
在写这道题的时候,我知道是要用二分法,但是思路理所当然的将重点放在了中间值上,如果以中间值作为更改查找的切入点,那么解题过程会变得非常繁杂,需要考虑的特殊情况非常多。在经历了错误思路的折磨后,再看题解和视频豁然开朗,找到了思路的正确走向以及注意事项,因为在思路正确之后,解题时还是会在某些细节上出问题。
LeetCode27移除元素
题目描述:给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。
不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并 原地 修改输入数组。
元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。
解题思路
- 这道题如果用暴力解法做需要两次嵌套for循环,因此想要降低这道题的时间复杂度则最好使用双指针法。
- 这道题的双指针分别为快慢指针,但解题思路的难点在于没有完全理解快慢指针所代表的含义。快指针用于对整个数组进行遍历,并找出等于val的数据;而慢指针则可以看作是一个新数组,接收的数据是原数组中不等于val的所有数据。
解题方法及注意点
方法一 | 暴力循环法
方法二 | 快慢指针法
总结
这道题虽然知道要用双指针去做,但是因为没能理解快慢指针的真正含义而使解题逻辑非常不清晰,在看完讲解后理解了其含义再写这道题就简单很多。