这是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++算法基础专栏文章 下一篇-位运算的实现
每周六更新一篇文章,内容一般是自己总结的经验或是在其他网站上整理的优质内容
点个赞,关注一下呗~