提示:写完文章后,目录可以自动生成,如何生成可参考右边的帮助文档
一、双指针算法
双指针算法,顾名思义,即使用两个指针的算法,如之前所学的快排、归并都是利用了双指针,双指针可以在一个序列中使用,也可以在两个序列中使用,可以把朴素算法O(n^2)的时间复杂度读降低为O(n)。
比如使用两个指针 i 和 j时,对 i 进行循环,保证 j 不会回头,只朝着一个方向移动。i 和 j 可以朝同一个方向扫描,也可以反方向扫描。有时候两个指针也可以用不同的速度前进,比如双指针判断链表成环,一个指针速度为1,另一个指针速度为2。
朴素算法思路模板
for(int i = 0 ,i < n ; i++){ //这里直接暴力思路
for(int j = 0, j < n; j++){
}
}
双指针算法思路模板
for(int i = 0 ,i < n ; i++){
while( j < i && check(i, j)) j++; //这里的核心是j不会每次都从头开始了
} //check函数就是判断对于目前的 i 和 j,j是否需要继续右移
1.最长连续不重复子序列问题
分析题目,这里需要两个指针,分别用来确定子序列的左边界、和右边界。这里的核心是让 j 只从前到后走一遍, i 指针为右边界,j指针为左边界。这里利用了一个辅助数组b[ ],用来存放数a[ i ]在目前的 [j,i]区间有几个数。b[a[ i ]] > 1 即为新加入的i和之前的数有重复了,通过右移 j , 保证[j,i]区间不重复。每次记录的res都是对于当前的 i ,寻找最小的 j,j 再小就会右重复数字了。
import java.util.Scanner;
public class Main{
public static void main(String[] args){
Scanner s = new Scanner(System.in);
int n = s.nextInt();
int res = 0; //用来存放结果
int[] a = new int[n];
int[] b = new int[100010];
for(int i = 0;i < n;i++){ //初始化数组
a[i] = s.nextInt();
}
for(int i = 0,j = 0;i < n;i++){
b[a[i]]++;
while(j < i && b[a[i]] > 1) { //s[a[i] > 1,即为i+1造成的重复还没有消除,j++
b[a[j]]--; //序列中去除a[j]
j++;
} //循环结束后,即为一个不含重复数字的子序列,判断是否变大
res = res > (i - j + 1) ? res:(i - j + 1); //更新序列长度
}
System.out.println(res);
}
}
2.数组元素的目标和
代码如下(示例):
这题还是用双指针的方法。下面两段代码,第一部分,对于暴力方法优化了一点,但是每次 i 更新 j 还是会从0开始移动,只不过移动的范围变小了。
import java.util.Scanner;
public class Main{
public static void main(String[] args){
Scanner s = new Scanner(System.in);
int n = s.nextInt();
int m = s.nextInt();
int x = s.nextInt();
int c = m;
int i=0,j=0;
int[] a = new int[n];
int[] b = new int[m];
for(i = 0; i < n;i++)
a[i] = s.nextInt();
for(i = 0; i < m;i++)
b[i] = s.nextInt();
for(i = 0;i<n; i++){
j = 0;
while(a[i] + b[j] < x && j < c-1) //这里必须是c-1,如果最后一个依然满足又多加了
j++;
// if(j==c)
// j--;
if(a[i]+b[j]==x)
break;
else
c = j+1;
}
System.out.println(i+" "+j);
}
这里数据一大还是超时了,关键在于不应该让指针j再回滚了,双指针的目标就是O(n)一遍走过。
需要进行优化,想办法让 j 不回头的只移动一遍,分析两个数组的特性。让其分别从两端开始移动,a数组从左边移动,最小的开始,b的 j 从右边开始。如果a[i]+b[j] > x, 则 j 向左移动,因为最小的 i 也不能满足其相加等于 x。可以直接删除这个数了,反之则右移 i ,直到找到正确的i和j最多O(n)即可完成查找。
import java.util.Scanner;
public class Main{
public static void main(String[] args){
Scanner s = new Scanner(System.in);
int n = s.nextInt();
int m = s.nextInt();
int x = s.nextInt();
int i=0,j=0;
int[] a = new int[n];
int[] b = new int[m];
for(i = 0; i < n;i++)
a[i] = s.nextInt();
for(i = 0; i < m;i++)
b[i] = s.nextInt();
i = 0 ;
j = m-1;
while(true){
if(a[i] + b[j] < x)
i++;
else if(a[i] + b[j] > x)
j--;
else
break;
}
System.out.println(i+" "+j);
}
}
2.数组元素的目标和
这个题目也很简单,两个序列对应两个指针 i 和 j 。直接套用模板即可。
import java.util.Scanner;
public class Main{
public static void main(String[] args){
Scanner s = new Scanner(System.in);
int n = s.nextInt();
int m = s.nextInt();
int[] a = new int[n];
int[] b = new int[m];
for(int p = 0;p < n;p++)
a[p] = s.nextInt();
for(int p = 0;p < m;p++)
b[p] = s.nextInt();
int t = 0;
boolean f = false;
int i = 0,j = 0;
for(i = 0, j = 0;i < n;i++){
while(j < m && a[i]!=b[j]) j++;
if(j < m && a[i] == b[j]){
t++;
j++;
}
if(j==m)
break;
}
if(t == n)
System.out.println("Yes");
else
System.out.println("No");
}
}