双指针是一种思路,很多题都可能用得到,这里我就只选取Acwing网站的三道题(事实上我最近就是在这里刷题,leetcode反而不怎么去了,刷完这个网站的我就会去leetcode刷了)
双指针一般来讲会在数组有序的情况下应用,但是如果是无序的也是有可能的,两个指针会遍历整个数组(如果条件允许的话)。
我觉得双指针有两种情况,我不知道别人是怎么分类的,我感觉我做过几种题目,一种是两个索引(指针的意思就是索引,并不一定要是数据类型的那个指针),一个在前一个在后,一个往后走一个往前走,根据条件移动直到两个指针相遇,这一种的两个指针目的就是遍历完整个数组。另一种是两个索引,但是两个都是在前面,一个先走另一个根据条件追上去,两指针相遇或者一个到了数组结尾就结束,这一种的数组可以是无序的,并且这一种的主要目的很有可能就是两个指针之间的区间。其他的就是多个数组形成了多指针这种类似的变种了(有新情况我就更新一下)
那么直接上题目。
1 .
思路:观察题目,题目一开始并没有说是有序的数组,那么直接当成无序的数组来看。题目的意思是找到不重复的数的连续区间,也就是说,我们在找的过程中我们的区间长度都是变动的,数组两端中间的数值不允许重复。其实从这里就可以确定是双指针了,因为双指针就是动态变化的,根据我分的第二类来看,另一个指针就是根据条件来看要不要移动,而这一个条件其实就是某一个数是否重复了。对于某一个数是否重复了,我们可以新建一个数组u,如果重复了那么设定成true,当然这一种我一般是在递归树题目才会使用,双指针的话可以新建一个频率数组,至于怎么用,看代码就可以理解了。
这里是图解,感谢大佬画的图解:
这里是代码:
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> using namespace std; //按照示例来做 , 要选择的区间是[i,j],i从0开始循环到n,j从0开始,j不一定循环到n //而是循环到重复的数的最后一个数(2,2,3,2,j指到第三个2就行),一旦j移动,那么 //区间的s也要删掉,如果不删掉的话while检查条件无法成立,而且题目要求是连续的序列 //j之后不会被赋值为0 //双循环改良,使用while来替代后循环,去掉一些不符合最终条件的数 //关于一些双指针,Bug-Tree大佬有笔记 //for(int i = 0 ; j = 0 ; i < n ; i ++){ // while(j < i && check(i , j)) j ++; // } const int N = 100010; int n; int a[N] , s[N]; int main(){ cin >> n; for(int i = 0 ; i < n ; i ++)cin >> a[i]; 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 = max(res , i - j + 1);//i - j + 1 是因为左闭右闭(建议回想下前缀和的区间),j // } cout << res; return 0; }
我们观察一下这个代码,其实双指针是有模板的(嗯,我觉得有就是有),我理解的模板:1.for循环,遍历数组。2.while过滤,while是核心代码。3.if条件,有时候有有时候没有,用来减少无意义代码或者是别的用途,按照题目来讲。我在第一道题的代码的注释里面也有提到在这个模板
我们来看第二道题目
2.
思路:很容易看出来是遍历数组来找到符合条件的,因为有多个数组,所以很容易想到双指针(哈哈),这一种做出来肯定很容易,难的是时间限制了吧,我们必须尽可能想到如何过滤掉一些没用的,嗯,就是if条件了,回想一下双指针的模板,以下是代码:
代码:
#include <iostream> #include <cstring> #include <cstdio> #include <algorithm> using namespace std; const int N = 100010; //观察了一下,这道题用双指针改良,其实就是我想的那样,在里面添加条件就可以了,我看了下题解,就是从后面往前,j还是要经过整个数组 //其实不太符合我内心对双指针的想法。 // //总结:判断条件是否要进去操作(以减少操作数据规模)和while过滤是双指针的核心 //双指针和双层循环,使用双层循环时候(如果该代码是核心代码的话)可以考虑可不可以使用双指针删掉可能存在的一些无意义 int n , m , x; int a[N]; int b[N]; int main(){ cin >> n>>m>>x; for(int i = 0 ; i < n ; i ++)cin >> a[i]; for(int i = 0 ; i < m ; i ++)cin >> b[i]; for(int i = 0 , j = m - 1 ; i < n ; i++){ while(j >= 0 && a[i] + b[j] > x) j--; if(j >= 0 && a[i] + b[j] == x) cout << i << " " << j << endl; } return 0; }
也是for+while+if。那么直接来看第三道题目吧
第三道题目
3.
思路:这一道题目明显就是要按照有序的数组来看,因为子序列的要求就是这样子的(),这一道题的思路就是两个数组两个指针,A数组的指针是遍历用的,按顺序遍历到A数组的尾部的过程中如果有和B数组一样的元素那么B数组的指标就会移动,如果A数组遍历完了B数组指针还没到尾部那么B数组就不是A数组的序列。因为是双指针我们直接看模板就可以了。
这里是代码:
//过早总结了,还有一题,同样需要过滤的while和操作的if条件 #include <iostream> #include <cstring> #include <cstdio> #include <algorithm> using namespace std; const int N = 100010; int n,m; int a[N]; int b[N]; int main(){ cin >>n>>m; for(int i = 0 ; i < n ; i ++) cin >> a[i]; for(int i = 0 ; i < m ; i ++) cin >> b[i]; int t = n;//t是a数组还没有被匹配的数量 for(int i = 0 , j = 0 ; i < n; i ++){ while(a[i] != b[j] && j < m ) j ++; //while进行过滤 if(j >= m) break;//过滤的时候发现j已经到头了那么就直接break吧,这一步容易没有考虑到 //匹配状态,其实暗藏着一个if条件a[i] == b[j] ,这是我们操作的条件 t --; j ++;//匹配到了那么就下一个 } //数量为0则全部匹配成功,不为0就说明b数组按照a数组匹配还没有匹配完b数组就没了,那么结束 if(t > 0) cout << "No"; else cout << "Yes"; return 0; }
标签:12,int,++,while,part1,算法,数组,include,指针 From: https://www.cnblogs.com/clina/p/17991506