第二期:双指针(基础算法) ✨
蓝桥杯4月8号就要开始了~
大家好 我是寸铁 ✨
蓝桥杯每日一题系列持续更新中,小伙伴们快上车!冲冲冲💘
预祝大家蓝桥杯都能上岸👊
冲刺蓝桥杯省一吧必背模板来啦~
往期回顾
蓝桥杯上岸必背!!!(第一期)
考点秘籍
蓝桥杯省一你一定不能错过的模板大全(第一期)
蓝桥杯每日一题 最少刷题数
蓝桥杯每日一题 求阶乘
蓝桥杯 Java 省赛 B 组(初赛)填空题
Java快读快写
java常用数组拷贝
常用Java竞赛基础技能总结
双指针和二分的覆盖面广!!!
且较为常用!!!
下面让我们一起来背模板~~~
双指针算法
一般分为两大类:
(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);
}
}
模板背完后,做做例题吧 👊
例题:AcWing 800. 数组元素的目标和
做法如下:
双指针
//由于两个都是升序数组
//存在单调性
//我们拿上面最小的元素去和下面最大的元素进行匹配
//因为最小的元素都匹配不了,则该序列中后面的元素必定匹配不了。
//如果说最小的元素匹配后的值比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;
}
}
}
}
AcWing 2816. 判断子序列
Accode
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/
👍👍👍👍👍👍
看到这里,不妨点个关注 💖