双指针算法是一种非常常用的算法,是对暴力算法的一种优化,其实现原理也非常简单且灵活,具体操作一般是定义两个指针对数组进行遍历等操作。
双指针算法有很多种情况,如下:
一.对撞指针(头尾指针)
头指针指向数组的第一个数字,尾指针指向数组的最后一个数字。
二.单数组同向指针(快慢指针)
两个指针指向同一个数组的第一个数字或者最后一个数字,一个指针走的快,一个指针走的慢。
三.双数组指针
这种情况多是两个指针分别指向两个不同的数组,分别进行遍历。
题目链接167. 两数之和 II - 输入有序数组
主要思路:设置头指针left和尾指针right。通过两个指针遍历找到目标数字。
分析:为什么这题需要用头尾指针?
假设该题使用两个指针同时指向数组的第一个数字。但是这样就会导致很难实现。
关于使用头尾指针
题目要求是在数组中找到两个数字的和==target。而定义头尾指针非常适合该题。这就像定义了一个规定长度的窗口,头指针+尾指针指向的数字之和=窗口值。通过改变窗口值来寻找匹对的target。
题解:
1. 初始化操作:一个left头指针,一个right尾指针
2. 设定初始的窗口值sum=头指针指向的数字+尾指针指向的数字
3. 通过窗口值与target的比较,对窗口的长度进行修改。因为该数列是一个递增数列,所以left指针向前走就会导致窗口值变大,right指针后退就会导致窗口值变小。
4. 最终遍历完成,left指针和right指针指向的数字就是最终答案
5. 这里的最终答案需要+1,因为数组是从0开始存储的,但是题目要求的是从1开始。
class Solution {
public:
vector<int> twoSum(vector<int>& numbers, int target) {
vector<int> v3;
int left=0;//(1)
int right=numbers.size()-1;//(2)
int sum;
sum=numbers[left]+numbers[right];//(3)
while(left<right){
if(sum==target)//(4)
break;
else if(sum<target){//(5)
sum-=numbers[left++];
sum+=numbers[left];
}
else if(sum>target){//(6)
sum-=numbers[right--];
sum+=numbers[right];
}
}
v3.push_back(left+1);
v3.push_back(right+1);
return v3;
}
};
(1):创建头指针
(2):创建尾指针
(3):设置初始的窗口值
(4):窗口值==target,说明找到了答案,退出循环
(5):窗口值小于target,说明需要扩大窗口,让头指针前进
(6):窗口值大于target,说明需要缩小窗口,让尾指针后退
题目链接剑指 Offer 48. 最长不含重复字符的子字符串
主要思路: 设置两个快慢指针分别指向数组的第一个元素,用一个辅助数组计算不同字符出现的次数。
题解:
1.初始化操作: 设置一个快指针i和慢指针j同时指向数组的第一个元素,一个数字全是0的辅助数组a[N]
辅助数组a[N]的作用:用于记录字符出现的次数,如a字符出现,那么辅助数组a[‘a’]这个位置的值就+1.当辅助数组某个位置的值出现两次时,就说明这个字符连续出现了两次。
2. 快指针i先走,i指针每走一步,辅助数组就将当前字符asall码位置的数字+1,表示这个字符出现了一次
3. 当相同的字符出现两次以后,慢指针会一直走到快指针的位置
4. 对当前出现的最长连续不重复子串进行更新。
const int N=40000;
class Solution {
public:
int lengthOfLongestSubstring(string s) {
int a[N]={0};
int res=0;
int j=0;
for(int i=0;i<s.size();i++){
a[s[i]]++;//(1)
while(a[s[i]]>1){//(2)
a[s[j]]--;
j++;
}
res=max(res,i-j+1);//(3)
}
return res;
}
};
(1):使用辅助数组存储当前字符的出现次数
(2):碰到重复字符,慢指针走到快指针位置
(3):更新当前的字符最优长度
题目链接判断子序列
主要思路:设置两个指针分别指向两个数组。
题解:
1. 初始化操作,设置两个指针i和j,i指针指向子串,j指针指向主串
2. 通过j指针遍历主串,当i指针指向的字符和j指针指向的字符相当时,i指针就向前走一步,最后遍历结束,判断i的大小是否等于主串的长度(如果i==主串的长度,说明字串的所有字符均在主串中出现过)。如果i的大小不等于字串的长度,说明该子串不属于该主串。
class Solution {
public:
bool isSubsequence(string s, string t) {
int i;
int j;
for(i=0, j=0;j<t.size();j++){//(1)
if(s[i]==t[j])//(2)
i++;
}
if(i==s.size()){//(3)
return true;
}
else
return false;//(4)
}
};
(1):通过i指针遍历主串
(2):i指针当前指向子串的字符==主串的字符,i指针向前走一步
(3):i的大小等于子串的长度,说明子串属于主串
(4):反之说明子串不属于主串。
1.判断子序列
2.167. 两数之和 II - 输入有序数组
3.剑指 Offer 48. 最长不含重复字符的子字符串
4.4297. 截断数组
5.剑指 Offer 57 - II. 和为s的连续正数序列