双指针
双指针有两个大类,一类是有两个数组,每个数组有一个指针,一类是一个数组,两个指针分别指向头和尾
通用写法
for(i=0,j=0;i<n;i++){
while(j<i && check(i,j)) j++;
//每道题目的具体逻辑
}
//核心思想
//对于暴力:
for(int i=0;i<n;i++)
for(int j=0;j<n;j++)
O(n^2)
//双指针 将上面朴素算法优化到O(n)
核心思想
最长连续不重复子序列
先想朴素做法,再想怎么优化

放上面红色指针往前移的时候,绿色指针一定不会往后移(不然就矛盾了,因为绿色指针移动逻辑是有重复的元素),因此当红色指针向右移动,绿色指针也一定是向右移动的,具有单调性,所以可以优化每次循环i看j要不要往右走
for(int i=0,j=0;i>n;i++){
while(j<=i&&check(j,i)) j++;//check判断j-i之间有没有重复元素
res =max(res,i-j+1);
}
用一个数组动态记录每个数字出现多少次
s[N];
每次i移动一格说明加入了新的数:s[a[i]]++;
每次j移动一格说明要减去a[j]出现的次数s[a[j]]–;
保持s[j,i]区间内每个数字都<=1,如果新加入的a[i+1]是重复的那么s[a[i+1]]>1,需要移动j
核心思路:
- 遍历数组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.util.Scanner;
public class Main {
public static void main(String[] args){
Scanner scan = new Scanner(System.in);
int n = scan.nextInt();
int[] a = new int[100010];
int[] s = new int[100010];
for(int i = 0 ; i < n ; i ++ ){
a[i] = scan.nextInt();
}
int res = 0;
for(int i=0,j=0; i<n; i++){
s[a[i]]++;//记录放入的数据
while(s[a[i]]>1){//有两个以上连续的看下一个是不是
s[a[j]]--;//
j++;
}
res=Math.max(res, i-j +1);
}
System.out.println(res);
}
}
数组元素的目标和
思路
i从 0开始 从前往后遍历
j从 m - 1开始 从后向前遍历
和<x就退出j的遍历
代码
import java.util.Scanner;
public class Main {
public static void main(String[] args){
Scanner scan = new Scanner(System.in);
int n = scan.nextInt();
int m = scan.nextInt();
int x = scan.nextInt();
int[] a = new int[100010];
int[] b = new int[100010];
for(int i = 0 ; i < n ; i ++ ){
a[i] = scan.nextInt();
}
for(int i = 0 ; i < m ; i ++ ){
b[i] = scan.nextInt();
}
for(int i=0,j=m-1;i<n;i++){
while(j>=0&&a[i]+b[j]>x) j--;//因为数组是有序的从后到前,小于就可以直接进入下一个i
if(j>=0&&a[i]+b[j]==x) System.out.printf("%d %d",i,j);
}
}
}
判断子序列
思路
每次扫描A,B两个数组判断当前元素是否一样,一样就看后面的元素,一直直到结束了如果A数组走到终点了就存在子序列
上面说明双指针可以找到一种匹配方式
还需要反过来:若存在一种不是双指针的匹配,则上述算法必然可以找出一个匹配。如下图实线代表原匹配,因为双指针可以找到第一个相等的匹配元素,即虚线部分,而不影响其他匹配,则证明前文正确。


代码
import java.util.Scanner;
public class Main {
public static void main(String[] args){
Scanner scan = new Scanner(System.in);
int n = scan.nextInt();
int m = scan.nextInt();
int[] a = new int[100010];
int[] b = new int[100010];
for(int i = 0 ; i < n ; i ++ ){
a[i] = scan.nextInt();
}
for(int i = 0 ; i < m ; i ++ ){
b[i] = scan.nextInt();
}
int i=0,j=0;
while (i < n && j < m){
if(a[i]==b[j]) i++;
j++;
}
if(i==n) System.out.printf("Yes");
else System.out.printf("No");
}
}
527

被折叠的 条评论
为什么被折叠?



