双指针算法及经典例题
- 双指针算法介绍
- 模板:
- 双指针的经典例子:输出单词
- 经典例题: 最长连续不重复子序列
双指针算法介绍
双指针算法有两类:
1.对撞指针 :两个指针分别指向不同的队列(归并排序)
2.快慢指针 : 两个指针指向一个队列(快排)
模板:
for(int i =0;i<n;i++){
for(int j =0;j<=i;j++){
if(check(j,i)){
res = max(res,j-i+1);
}
}
}
for(int i =0,j=0;i<n;i++){
while(j<i&&check(i,j)){
j++;
}
}
所有的双指针算法都是o(n*n),将朴素算法优化到o(n)
双指针的经典例子:输出单词
题目:输入I Love you (中间有空格)
输出:
I
Love
you
public class I {
public static void main(String[] args){
//method one
String s = "I Love you";
String [] s1 = s.split(" ");
// System.out.println(s1[0]);
// System.out.println(s1[1]);
// System.out.println(s1[2]);
//method two
for(int i =0;i<s.length();i++){
int j =i;
while(j<s.length()&&s.charAt(j)!=' '){
j++;
}
for(int k =i;k<j;k++){
System.out.print(s.charAt(k));
}
System.out.println();
i = j;
}
}
}
经典例题: 最长连续不重复子序列
给定一个长度为n的整数序列,请找出最长的不包含重复的数的连续区间,输出它的长度。
输入格式
第一行包含整数n。
第二行包含n个整数(均在0~100000范围内),表示整数序列。
输出格式
共一行,包含一个整数,表示最长的不包含重复的数的连续区间的长度。
数据范围
1≤n≤100000
输入样例:
5
1 2 2 3 5
输出样例:
3
解题思路:
核心思路:
遍历数组a中的每一个元素a[i], 对于每一个i,找到j使得双指针[j, i]维护的是以a[i]结尾的最长连续不重复子序列,长度为i - j + 1, 将这一长度与r的较大者更新给r。
- 对于每一个i,如何确定j的位置:由于[j, i - 1]是前一步得到的最长连续不重复子序列,所以如果[j, i]中有重复元素,一定是a[i],因此右移j直到a[i]不重复为止
- 由于[j, i - 1]已经是前一步的最优解,此时j只可能右移以剔除重复元素a[i],不可能左移增加元素,因此,j具有“单调性”、本题可用双指针降低复杂度)。
- 用数组s记录子序列a[j ~ i]中各元素出现次数,遍历过程中对于每一个i有四步操作:cin元素a[i] -> 将a[i]出现次数s[a[i]]加1 -> 若a[i]重复则右移j(s[a[j]]要减1) -> 确定j及更新当前长度i - j + 1给r。
注意细节:
当a[i]重复时,先把a[j]次数减1,再右移j。
画个图:
import java.io.*;
class Main{
static int []nums = new int[100005];
static int []arr = new int[100005];
public static void main(String []args) throws Exception{
BufferedReader read = new BufferedReader(new InputStreamReader(System.in));
String s1 = read.readLine();
int n =Integer.valueOf(s1);
String[] s2 = read.readLine().split(" ");
for(int i = 0;i<n;i++){
nums[i] = Integer.valueOf(s2[i]);
}
int max = 0;
for(int i = 0,j=0;i<n;i++){
//记录出现次数
arr[nums[i]]++;
while(arr[nums[i]]>1){
//由于j每次加一位,所以将前一位减去一
arr[nums[j]]--;
j++;
}
max = Math.max(i-j+1,max);
}
System.out.println(max);
}
}