【C++/Java】详解双指针算法以及与暴力的区别-有序数组和与最长不重复子序列

1、双指针算法

        双指针算法:也就是有两个指针i和j,i和j具有一定的单调关系或者存在某种关系,可以使得当j满足什么条件时候,j会往前移动,注意,双指针算法的时间复杂度是O(n),也就是i和j都不会重复扫到一个元素,这里区别于双重循环的O(n^2),在双重循环中,我们的i或者j是有可能一个元素扫描好几次的,一般是n次或者n(n-1)/2或者n(n+1)/2,这里看具体算法了。(许多人认为一层循环嵌套另一层循环,那么时间复杂度就是O(n^2),其实不然,也就是因为这是错误理解,时长分不清双指针和暴力的区别)

接下来我们以两个数组和与最长不重复子序列为例子,详细巩固双指针算法:

1.1、有序数组和

        问题: 在给定的一个有序数组中,查找是否存在两个元素的和等于目标值target,这是一个常见的双指针算法,思路日常简单,把i和j指向数组两端,当满足data<target时候i++等等这些属于性质,时间复杂度是O(n);

        

#include<iostream>
using namespace std;

const int N = 1e6+10;

int a[N];

bool checktarget(int a[],int n,int target){
	int i=0,j=n-1;
	while(i<j){
		int data=a[i]+a[j];
		if(data == target){
			return true;
		}
		if(data<target){
			i++;
		}else{
			j++;
		}
	}
	return false;
}

int main(){
	int n,m;//n是有序数组长度,m是询问次数 
	scanf("%d%d",&n,&m);
	for(int i=0;i<n;i++){
		scanf("%d",&a[i]);
	}
	int target;
	while(m--){
		printf("请输入需要寻找的目标值:");
		scanf("%d",&target);
		if(checktarget(a,n,target))printf("存在");
		else printf("不存在"); 
	}
}
 

1.2、最长不重复子序列

        问题:给定一段序列,请你找出最长不重复子序列,例如

        1 3 2 2 3 4 5        ->结果:2 3 4 5

        最长不重复子序列也是典型的双指针的应用算法,滑动窗口的思路进行。

        本题就是典型的双循环嵌套,但是因为i和j存在单调关系,那么最坏时间复杂度也只是O(2n),在全部是一个数的情况下,最长不重复子序列是1,那么i和j会同时在n-1的地方结束,并且期间是不会重复去访问同一个数目的,这就是双指针和暴力的本质区别。

C++实现:

#include <iostream>
using namespace std;
const int N=1e6+10;

int a[N],s[N];
//利用哈希表的思路来做,s[N]把它看做哈希表,也可以自己导入扩展使用哈希表。 
int main(){
	int n;
	scanf("%d",&n);
	for(int i=0;i<n;i++){
		scanf("%d",&a[i]);
	}
	int res=0;
	for(int i=0,j=0;i<n;i++){
		s[a[i]]++;//存储数
		while(j<i && s[a[i]]>1)s[a[j++]]--;//这时候说明有重复的数了,让j滑动到重复的序列那
		res=max(res,i-j+1); 
	}
	printf("%d",res);
}

Java实现

import java.util.Scanner;

/*
*
* 最长不重复子序列
*
* */
public class DoublePoint {
    public static final int N=100000;
    public static final int[] a=new int[N],s = new int[N];

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int n = scanner.nextInt();
        for(int i=0;i<n;i++){
            a[i]=scanner.nextInt();
        }
        int res=0;
        for(int i=0,j=0;i<n;i++){
            s[a[i]]++;//类似哈希表,存储个数的
            while(j<i && s[a[i]]>1)s[a[j++]]--;//当录入的值有大于1,也就是出现重复的数目了,把j移动到当前的位置,这里s[a[j++]]--是因为当到达重复序列的时候一直要把重复序列个数减到位置,不然到不了那个位置
            res = Math.max(res,i-j+1);
        }
        System.out.println(res);
    }
}

  • 12
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

努力奋斗的张同学

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值