第二期:双指针(基础算法) 
蓝桥杯4月8号就要开始了~
还没背熟模板的小伙伴们跟我一起来背模板吧 


祝大家4月8号蓝桥杯上岸
~
不清楚蓝桥杯考什么的点点下方
想看往期模板的点点下方
想看JavaB组填空题的伙伴们点点下方 
双指针和二分的覆盖面广!!!
且较为常用!!!
下面让我们一起来背模板~~~
双指针算法
一般分为两大类:
(1)两个序列中移动i、j指针
在两个序列中分别移动i、j
指针
当两个指针均移动到终点时,双指针算法结束。
常见的是归并排序的合并过程。
(2)一个序列中移动i、j指针
固定一个指针,然后去移动另一个指针
常见:快速排序。
模板结构:
for(int i=0,j=0;i<n;i++){
while(j<i&&check(i,j))j++;
//每道题目的具体逻辑
}
核心思想:将暴力算法优化到O(n)
挖掘某些性质,减少枚举的次数。
直接暴力两重循环:O(n^2)
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
//
}
}
双指针优化:
O(2*n)=O(n)
for(int i=0,j=0;i<=n;i++){
while(j<i&&check(i,j))j++;
}
双指针算法是一种思想,无固定的模板,这里以模板题为例
分析
两个指针i,j
:左指针j
距离右指针i
最靠左的距离为多少
右指针往前移动,左指针维持不动或者往前移动,不会往后移动。
可以得出i、j
的移动具有单调性
因此可以用双指针算法去移动两个指针
while()
移动j
指针,移动直至j~i
这段区间都无重复元素为止。
即s[a[i]]=1
时
注意
while()
:多次循环
if()
判断语句:执行一次
最长连续不重复子序列
//对一段序列用两个指针实现不同位置的同步移动
import java.util.*;
public class Main{
static int N=100010;;
static int a[]=new int [N];
//输入数组
static int s[]=new int [N];
//统计序列中每个a[i]出现的次数
public static void main(String []args){
Scanner sc=new Scanner(System.in);
int n=sc.nextInt();
int res=0;
for(int i=0;i<n;i++)a[i]=sc.nextInt();
for(int i=0,j=0;i<n;i++){
s[a[i]]++;
//对于i~j的这段不重复数的区间
//如果是出现重复数一定是出现在i的位置
//即s[a[i]]>1
//注意是While()循环,执行多次判断,移动j指针。
//写成if的话,是不会一直移动j指针
while(j<=i&&s[a[i]]>1){
s[a[j]]--;
//从j~i这段区间一直除,直至s[a[i]]的个数为1
j++;
//每除一次,j往i的方向移动一次
//除到s[a[i]]=1时,j不再移动
//此时j~i这段区间的数均不重复
}
res=Math.max(res,i-j+1);
//更新到每个i的最大不连续区间长度
//可以理解为j最左离i的距离是多少
}
System.out.println(res);
}
}
模板背完后,做做例题吧 
双指针
//由于两个都是升序数组
//存在单调性
//我们拿上面最小的元素去和下面最大的元素进行匹配
//因为最小的元素都匹配不了,则该序列中后面的元素必定匹配不了。
//如果说最小的元素匹配后的值比x要大,则移动j指针
//j指针移动至和值小于等于x时停止,移动i后面的数能否匹配。
//匹配过程出现和值>=x时,则继续移动j指针。
import java.util.*;
public class Main{
static int N=100010;
static int a[]=new int[N];
static int b[]=new int[N];
public static void main(String []args)
{
Scanner sc=new Scanner(System.in);
int n=sc.nextInt();
int m=sc.nextInt();
int x=sc.nextInt();
for(int i=0;i<n;i++)a[i]=sc.nextInt();
for(int i=0;i<m;i++)b[i]=sc.nextInt();
for(int i=0,j=m-1;i<n;i++){
while(j>=0&&a[i]+b[j]>x)j--;
if(a[i]+b[j]==x){
System.out.println(i+" "+j);
return;
}
}
}
}
二分
import java.util.*;
public class Main{
static int N=100010;
static int a[]=new int [N];
static int b[]=new int [N];
public static void main(String []args){
Scanner sc=new Scanner(System.in);
int n=sc.nextInt();
int m=sc.nextInt();
int x=sc.nextInt();
for(int i=0;i<n;i++)a[i]=sc.nextInt();
for(int j=0;j<m;j++)b[j]=sc.nextInt();
for(int i=0;i<n;i++){
int t=x-a[i];
//看b序列中是否能二分出答案
//枚举n次
//看能否在b序列中找到
int l=0,r=m-1;
//确定左右边界
while(l<=r){
//这里需要枚举b序列的每一个位置
//l<=r
//开始二分
int mid=l+r>>1;
//二分的中间点
if(b[mid]==t){//二分的答案
System.out.println(i+" "+mid);
//a序列的位置+" "+b序列的位置
return;
//唯一解,找到就返回
}
else if(b[mid]>t)r=mid-1;
//mid这个值比t要大
//b[mid]已经被比较过,所以r=mid-1;
else l=mid+1;
//mid这个值比t要小
//b[mid]已经被比较过,所以l=mid+1;
}
}
}
}
import java.util.*;
public class Main{
static int N=100010;
static int a[]=new int[N];
static int b[]=new int[N];
public static void main(String []args){
Scanner sc=new Scanner(System.in);
int n=sc.nextInt();
int m=sc.nextInt();
for(int i=0;i<n;i++)a[i]=sc.nextInt();
for(int j=0;j<m;j++)b[j]=sc.nextInt();
//指针的定义不一定需要for循环
//可以单独定义两个指针然后while循环
int i=0;
int j=0;
while(i<n&&j<m){
//在两个序列的范围内
if(a[i]==b[j])i++;
//找到匹配的情况则i往下走,看i是否可以匹配
j++;
//j一直++,往前移动,看是否能够匹配
}
//最后一个i=n-1,n-1匹配上j后,则i++后等于n
//如果刚好i等于n则说明a序列刚好与b序列匹配,输出Yes。
//这也是一个小技巧
if(i==n)System.out.println("Yes");
else System.out.println("No");
}
}
Java竞赛常用
https://www.myblog.link/2016/11/14/Note-of-java/