本文将针对双指针的使用场景进行讲解。
双指针作用
双指针指使用两个指针对数组进行操作,一般一个放首,一个放尾,或者两个都放在开头,移动速度或开始移动的时机不同。
常见的使用场景包括:
- 两数求和(或者三数求和):寻找两个数的和为一个特定的值
- 数组就地交换
- 正负数排序数组平方和处理(LeetCode977)
- 删除符合条件的特定元素(字节跳动笔试题)
双指针难点总结
双指针在使用中往往会遇到如下问题:
- 每个指针分别代表什么含义
- 如何移动每个指针
本文将针对双指针中常见的一些应用场景下的含义及移动方法进行总结。
起始位置
- 两个指针均从头开始移动
- 一头一尾移动
移动方法
- 如果符合条件,两个指针均向后移动,否则移动快指针
- 快指针以慢指针k倍速移动
双指针指向数组两端,分别向中间收缩,用于解决两数求和的问题
注意事项
- 使用前需要对数组排序
- 循环判断条件为while(left < right)
- 可能需要去除重复
思路
和大于K,则右指针左移,否则左指针右移
1 | for(int i=0;i<nums.size()-3;i++){ |
双指针延伸方法
滑动窗口法,用于搜索符合条件的连续子串
滑动窗口实际上就是一个队列,队列的长度根据符合条件的子串的长度,是不断发生变化的,通过遍历整个数组,就能找到符合条件的连续子串。我们一般使用双指针构成滑动窗口,将i,j分别指向窗口左右边界,通过移动i和j,即可将窗口进行滑动。
LeetCode第3题 最长不含重复字符的子字符串
给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度。
1 | 输入: "abcabcbb" |
代码
1 | // 解法1 |
给你一个有序数组 nums ,请你原地删除重复出现的元素,使每个元素 最多出现$k$次 ,返回删除后数组的新长度。
不要使用额外的数组空间,你必须在原地修改输入数组 并在使用 O(1) 额外空间的条件下完成。
这个题思路非常巧妙,我们分成两种情况讨论:
- 对于前$k$个数字,我们可以直接保留
- 而对于后面的数字,我们采用局部性原理处理,仅关注
[len-k,len-1]
这一段,如果当前数字(num)与当前写入位置前第k
个元素不相同,那么可以保留,否则丢弃(num向后移动)
1 | int removeDuplicates(vector<int>& nums, int k) { |
双指针适用场景
子序列问题
给定字符串 s 和 t ,判断 s 是否为 t 的子序列。
你可以认为 s 和 t 中仅包含英文小写字母。字符串 t 可能会很长(长度 ~= 500,000),而 s 是个短字符串(长度 <=100)。
字符串的一个子序列是原始字符串删除一些(也可以不删除)字符而不改变剩余字符相对位置形成的新字符串。(例如,”ace”是”abcde”的一个子序列,而”aec”不是)。
1 | //子序列实际上是在一个固定的解空间中按照一定顺序组成一个解,在此处应用到了两个指针,i和j,i指向解,j指向解空间,如果找到了解,那么两个指针都递增,否则只递增解空间的指针,代表后向搜索 |
专题:字符串压缩
在有些情况下,需要我们对字符串进行压缩处理,如果能够实现原地修改算法,那么我们的字符串处理起来会很简单,本文将针对字符串压缩处理相关问题进行总结。
双指针例题
字节跳动2019春招笔试题——字符串处理
三个同样的字母连在一起是拼写错误,去掉一个:比如 helllo -> hello
两对一样的字母(AABB型)连在一起是拼写错误,去掉第二对的一个字母:比如 helloo -> hello
上面的规则优先“从左到右”匹配,即如果是AABBCC,虽然AABB和BBCC都是错误拼写,应该优先考虑修复AABB,结果为AABCC
输入例子1:
1 | 2 |
输出例子1:
1 | hello |
解答
1 |
|
LeetCode 15 三数求和
给定一个包含 $n$ 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?找出所有满足条件且不重复的三元组。
1 | class Solution { |
解法很简单,可以将三数求和转换为两数求和问题,即num[i] + num[j] = num[k],做一些去重复处理即可