这是C++算法基础-基础算法专栏的第十五篇文章,专栏详情请见此处。
ps:转眼间暑假已过半,我在这段时间也积累了很多文章,所以到开学(9月1日)为止,每个周我将会在周三和周六发文章(o゚▽゚)o
引入
双指针是一种解决问题的一种方法,我们可以在题目中挖掘一些性质,并使用双指针算法优化时间复杂度。
下面我们就来讲双指针算法的实现。
定义
双指针是一种简单而又灵活的技巧和思想,单独使用可以轻松解决一些特定问题,和其他算法结合也能发挥多样的用处。
双指针顾名思义,就是同时使用两个指针,在序列、链表结构上指向的是位置,在树、图结构中指向的是节点,通过或同向移动,或相向移动来维护、统计信息。
过程
双指针算法,从名字上就能看出,我们是用两个指针对序列进行操作的,具体有两种分类:
- 对于一个序列,用两个指针维护一段区间
- 对于两个序列,维护某种次序,比如归并排序中合并两个有序序列的操作
而双指针算法还可以通过问题类型分类,较简单与常见的有三种,接下来我们就用三道题目来详细讲解:
1. 维护区间信息
如果不和其他数据结构结合使用,双指针维护区间信息的最简单模式就是维护具有一定单调性,新增和删去一个元素都很方便处理的信息。
例题:AcWing-799. 最长连续不重复子序列
题目大意:给定一个长度为的整数序列,请找出最长的不包含重复的数的连续区间,输出它的长度。
具体思路:在一个区间中维护两个指针,遍历每一个位置,记录当前序列起始位置。首先,两个指针从头开始;然后进行遍历,并开一个额外的数组记录区间中每个数字出现的次数(就是开一个桶),每次移动时,桶内的数加一;然后判断,如果此时当前桶数字大于1,就说明目前指针所指的位置重复了;这时,记录序列长度,即,打擂台求出最大值,然后数组不断向前移动,将中存储的数字出现的次数减去1,直到遇到为止;最后,存储最大值的变量记录的就是最长连续不重复子序列的长度。
2. 维护有序序列
很多时候在序列上使用双指针之所以能够正确地达到目的,是因为序列的某些性质,最常见的就是利用序列的有序性。
例题:AcWing-800. 数组元素的目标和
题目大意:给定两个升序排序的有序数组和,以及一个目标值,求出满足的数对。
具体思路:由于两数之和固定,那么两数之中的小数越大,大数越小,那我们不妨从两个数组的两边接近它们。首先,两个指针分别位于的号位和的号位;然后先开始往后移动,此时会逐渐变大,直到,此时调整的位置(往前移动),直到不大于,当调整完后,进行判断,如果,那么就找到了答案,结束循环;如果,那么继续调整的位置,最后两者将会逼近到答案。
3. 子序列匹配
例题:AcWing-2816. 判断子序列
题目大意:给定一个长度为的整数序列以及一个长度为的整数序列,请你判断序列序列是否为序列的子序列。
具体思路:维护两个指针,从数组开始位置遍历,从数组开始位置遍历;先开始往后移动,直到,将同时加一,再继续遍历;最后,当数组遍历到最后一位时,说明已经比对完毕,只用看看数组有没有遍历完,如果遍历完毕,则说明序列是为序列的子序列,反之不是。
性质
时间复杂度
双指针算法的时间复杂度一般是的。
Q:双指针算法有两个指针,代码上也是两重循环,看似是的时间复杂度,为什么时间复杂度是的呢?
A:最最核心的就是,两个指针都是同时遍历的,他们只会从头走到尾,因此时间复杂度是,在量级。
代码
下面给出双指针算法的实现代码:
for(int i=0,j=0;i<n;i++){while(j<i&&check(i,j)) j++;}
代码解释
check()函数的作用是检查i,j是否满足某种性质;while循环下一行是写具体问题的逻辑的。
上一篇-二维差分的实现 C++算法基础专栏文章 下一篇-位运算的实现
每周六更新一篇文章,内容一般是自己总结的经验或是在其他网站上整理的优质内容
点个赞,关注一下呗~