two Pointers入门
给定一个递增序列
A[6] = {1,2,3,4,5,6};
求序列中两个任意元素下标 i 和 j ,令这两个元素的和为某个特定的值m
正常做只需用两重for循环遍历该数组求和然后输出结果即可
for(int i = 0; i < n; i++)
{
for(int j = i+1 ; j < n; j++)
{
if(A[i] + A[j] == m)
printf("%d", i , j);
}
}
算法时间复杂度为O(n2)
当n = 10 5甚至更多时该方法必然会相当的慢
通过仔细思考,我们可以发现:
- 当A[i] + A[j] >m时, 一定有A[i] + A[j+1]> m,所以 j+1到n-1的遍历都是无效遍历
- 当A[i] + A[j] >m时,一定有A[i+1] + A[j]> m,所以 i+1到n-1的遍历都是无效遍历
提示:该数组A为递增数组,且为一维数组,不懂的可以随便取个数m在纸上画一画A的遍历过程即可
我们将 i 设在起始位置 0 ,j 设为末尾位置 n-1:
1 A[i] + A[j] == m时, A[ i + 1] +A[ j ] > m 且 A[ i ] +A[ j- 1] < m
但A[ i + 1] + A[ j - 1 ]与m的大小关系是什么呢?我们已经知道前面控制一个变量增减时m的情况,此时两个变量都发生了变化,我们无法知晓他们之间的大小关系,是 > < 或是=呢? 我们可以把[ i + 1] 和 [ j - 1 ]设为下一个待定范围
2. A[i] + A[j] > m时, 有A[ i + 1 ] + A[ j ] > m恒成立,但 A[ i ] +A[ j- 1] 与m的关系我们不知道,所以将 范围缩小到 [ i, j-1 ]
3. A[i] + A[j] < m时, A[ i ] +A[ j- 1] < m恒成立,同样我们不知道A[ i + 1 ] + A[ j ]与m的大小关系,此时把范围缩小到[ i + 1, j ]
不懂的建议继续草稿纸上画一下
我这里是把 i 变成了left, j变成了 right
#include <iostream>
#include<cstdio>
using namespace std;
int A[6] = {1,2,3,4,5,6};
const int arrayLen = 5;
int twoPointers(int m)
{
int left = 0, right = arrayLen;
while(left<= right)
{
if(A[left] + A[right] == m)
{
printf("%d %d\n", A[left], A[right]);
left++;right--;
}
else if(A[left] + A[right] < m)
{
left++;
}
else
{
right--;
}
}
}
int main()
{
int m = 5;
twoPointers(m);
return 0;
}
算法时间复杂度为O(n)
序列合并问题
假设有两个递增序列A和B,要求将他们合并为一个递增序列C
做法:
分别设置两个下标i j 在A和B中
A[ i ] > B[ j ]时,将B[ j ]放入序列C中
A[ i ] < B[ j ]时,将A[ i ]放入序列C中
A[ i ] == B[ j ]时,将A[ i ]或B[ j ]放入序列C中
当 i 或 j 达到末尾时就把另外一个数组其余部分放进C中
上代码
int A[6] = {1,3,4,5,7,9};
int B[7] = {1,2,4,6,8,10, 12};
int C[13];
void append()
{
int i = 0, j = 0;
int k = 0;
while(i < 6 && j < 7)
{
if(A[i] > B[j])
C[k++] = B[j++];
else
C[k++] = A[i++];
}
while(i < 6) C[k++] = A[i++];
while(j < 7) C[k++] = B[j++];
for(int i = 0; i< 13; i++)
printf("%d ", C[i]);
}