一、前言
一般情况下,遍历数组(或者字符串)操作,都是采用单指针从前往后或者从后往前依次访问数组(或者字符串)中的元素。
而对于以下情况,只采用单指针处理,则会徒增时间复杂度和空间复杂度:
例如:找到两个数使得它们相加之和等于目标数,采用单指针处理,则需要嵌套循环,使得时间复杂度增长为 O(n^2);
再例如:翻转数组,采用单指针处理,则需要额外内存空间,使得空间复杂度增长为 O(n);
利用双指针技巧则可以优化上述解决方案:
第一个例子:可以先对采用数组进行排序预处理,再创建前后指针向中间遍历,遍历的时间复杂度为 O(n),整体时间复杂度主要取决于排序算法,通常为 O(nlogn);
第二个列子:一个指针负责遍历,另外一个指针负责交换元素,从而使得空间复杂度为 O(1);
双指针没有复杂的定义,总结起来主要处理两类问题:
将嵌套循环转化为单循环问题;
通过指针记录状态,从而优化空间复杂度;
下面的实战分析会让你感受双指针的威力。
1、 两数之和 II – 输入有序数组
给定一个已按照升序排列 的有序数组,找到两个数使得它们相加之和等于目标数。函数应该返回这两个下标值 index1和index2,其中index1 必须小于 index2。
这道题目采用单指针的做法只能通过嵌套循环枚举所有两数之和的方法来解决,时间复杂度为 O(n^2)。
恰巧本题中的数组已经是有序数组,那么直接创建前后指针:
如果两数之后大于 target,尾指针向前移动;
如果两数之和小于 target,头指针向后移动;
上述代码利用双指针技巧成功地将时间复杂度降低为 O(n)。
const twoSum = (numbers, target) => {
const max = numbers.length
let start = 0
let end = max -1
while(start< end) {
const sum = numbers[start] + numbers[end]
if(sum === target)
{
return [start, end]
}
if(sum > target){
end --
continue
}
if(sum < target) {
start++
continue
}
}
}
// 这个文章里的两数之和是第一篇文章的升级版
// 时间复杂度:O(n)
// 空间复杂度: O(1) 比map 更加的高级
复制代码
26、删除排序数组中的重复项
给定一个排序数组,你需要在 原地 删除重复出现的元素,使得每个元素只出现一次,返回移除后数组的新长度。
不要使用额外的数组空间,你必须在 原地 修改输入数组 并在使用 O(1) 额外空间的条件下完成。
示例 1:给定数组 nums = [1,1,2],函数应该返回新的长度 2, 并且原数组 nums 的前两个元素被修改为 1, 2。
你不需要考虑数组中超出新长度后面的元素。
示例 2:给定 nums = [0,0,1,1,1,2,2,3,3,4],函数应该返回新的长度 5, 并且原数组 nums 的前五个元素被修改为 0, 1, 2, 3, 4。
说明:
为什么返回数值是整数,但输出的答案是数组呢?
请注意,输入数组是以「引用」方式传递的,这意味着在函数里修改输入数组对于调用者是可见的。
解题思路:
使用快慢指针来记录遍历的坐标。开始时这两个指针都指向第一个数字,如果两个指针指的数字相同,则快指针向前走一步。如果不同,则两个指针都向前走一步,当快指针走完整个数组后,慢指针当前的坐标加 1 就是数组中不同数字的个数。
const removeDuplicates = nums => {
const max = nums.length
if (max === 1) {
return nums
}
let slow = 0
for (let fast = 1; fast < max; fast++) {
if (nums[fast] !== nums[slow]) {
slow++
nums[slow] = nums[fast]
}
}
return slow + 1;
}
// 时间复杂度:O(n)
// 空间复杂度:O(1)
复制代码
总结
刷题打卡第二天,选择双指针,学习了提升代码效率的重要手段之一:前一次优化后的哈希map解法就是采用了空间换时间的方法。 但是双指针可以更加优化,但是前提是数组必须为有序的,今天又刷了力扣的第26题,一起加油哇~
❤️ 感谢大家
如果你觉得这篇内容对你挺有有帮助的话:
点赞支持下吧,让更多的人也能看到这篇内容(收藏不点赞,都是耍流氓 -_-)关注公众号给npy的前端秘籍,我们一起学习一起进步。
觉得不错的话,也可以阅读其他文章(感谢朋友的鼓励与支持???)
参考:
双指针技巧Easy篇