算法原理:
双指针算法的核心原理在于同时移动两个指针,通过控制指针的移动方式来达到特定的目的。常见的双指针算法有两种类型:
-
快慢指针:一个指针移动速度较快,另一个指针移动速度较慢,通常用于解决链表中的问题,比如判断链表是否有环、找到链表的中间节点等。
-
左右指针:两个指针分别从序列的两端开始向中间移动,通常用于解决数组或字符串中的问题,比如在有序数组中查找两数之和、反转字符串、判断回文串等。
算法步骤:
双指针算法的一般步骤如下:
-
初始化指针位置:根据具体问题,初始化两个指针的起始位置,通常是数组或序列的起始位置或结束位置。
-
移动指针:根据特定的条件,移动指针的位置,以便满足问题的要求。移动的方式可以是同时向前移动、一前一后移动、交替移动等。
-
判断终止条件:在移动指针的过程中,需要不断判断是否满足问题的终止条件,如果满足则停止移动。
-
处理问题:根据指针的位置和移动情况,解决问题并更新结果。
-
返回结果:返回问题的解或所需的结果。
双指针算法通常具有时间复杂度为O(n)的特点,因此在解决某些问题时具有高效的优势。
最长连续不重复子序列问题:
给定一个长度为n(1 <= n <= 100000)的整数序列,请找出最长的连续子区间,使得其中不包含重复的数,并输出该子区间的长度。
代码:
#include<iostream>
using namespace std;
const int N = 100010;
int s[N], cnt[N], n; // 定义数组和计数数组
int main()
{
cin >> n; // 读取数组长度
for(int i = 1; i <= n; i++) // 读取数组元素
cin >> s[i];
int max_num = 0; // 最长连续子数组的长度
for(int i = 1, j = 1; i <= n; i++) // 使用双指针维护滑动窗口
{
cnt[s[i]]++; // 更新计数数组
while(cnt[s[i]] > 1) // 当出现重复元素时,移动左指针直到没有重复元素
cnt[s[j++]]--;
// 更新最长连续子数组的长度
if(max_num < i - j + 1)
max_num = i - j + 1;
}
// 输出最长连续子数组的长度
cout << max_num;
return 0;
}
找出两个不同数组的数,使它们的和等于目标值问题:
给定两个升序排序的有序数组A和B,以及一个目标值x,找出满足A[i]+B[j]=x的数对(i,j),其中i和j分别是A和B中的下标,且数据保证有唯一解。
代码:
#include<iostream>
using namespace std;
const int N = 100010;
int a[N], b[N], n, m, x;
int main()
{
// 读取输入的数组长度、目标值以及数组元素
cin >> n >> m >> x;
for(int i = 1; i <= n; i++)
cin >> a[i];
for(int i = 1; i <= m; i++)
cin >> b[i];
// 初始化两个指针分别指向数组a和数组b的起始位置和末尾位置
int i = 1, j = m;
// 双指针遍历数组a和数组b
while(i <= n && j >= 1)
{
int t = a[i] + b[j]; // 计算当前两个数的和
if(t == x) // 如果等于目标值x
{
cout << i - 1 << " " << j - 1; // 输出找到的数对的下标
break;
}
else if(t > x) // 如果和大于目标值x,则向左移动数组b的指针
j--;
else // 如果和小于目标值x,则向右移动数组a的指针
i++;
}
return 0;
}
双指针算法是一种常用的解决问题的技巧,通常用于在序列(如数组、链表)中寻找满足特定条件的子序列或子数组。其核心思想是使用两个指针在序列中移动,以达到查找、更新或维护某种关系的目的。
总结:
-
思想简单:双指针算法的思想简单直观,易于理解和实现。
-
节省空间:双指针算法通常只需要常数级别的额外空间复杂度。
-
时间复杂度低:双指针算法通常具有线性时间复杂度O(n),因此在解决某些问题时具有高效的优势。
-
适用范围广泛:双指针算法适用于解决很多问题,比如在数组中查找满足某种条件的子数组、在链表中找到环的入口等。