4.6.1什么是双指针?
双指针是一个编程技巧。由于是一个编程技巧,所以更适合结合着题练习。
例 一:
给定一个递增的正整数序列和一个正整数M,求序列的两个不同位置的数 a 和 数 b,使得它们的和恰好为M,输出所有的满足的方案。例如给定序列{1,2,3,4,5,6}和 正整数M=8,就存在 2+ 6= 8 和 3+5=8.
本题的一个很直观的想法是双重for循环,这里不多赘述。这种做法的时间复杂度为O(n^2),对于n在 10^5的规模是不可承受的。
我们来看看这种做法的复杂度高的原因:
1.对于一个确定的a[i],如果a[i] + a[j] > M,显然a[i] + a[j+1]>M,因此就不需要对a[j]之后的数进行枚举。
2.对于某个a[i]来说,如果找到一个a[j],使得a[i] + a[j] > M ,恰好成立,那么a[i+1] + a[j] >M,
所以我们也不需要进行枚举。
我们可以看出,i 和 j 似乎有种某种联系,因此本题我们采用双指针法:
它针对本题的算法如下:
令 i =0 , j = n - 1.及i指向第一个元素,j指向最后一个。利用 a[j] + a[i] 与M的关系来不断进行判断。使i不断右移,j不断左移,知道i>=j成立。
1.如果a[i] + a[j] =M,说明找到了一组方案,由于数列地政a[i + 1] + a[j] > M, a[i] + a[j - 1] <M 成立,但是a[i+1] + a[j -1]的大小未知,因此接下来就在[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]中找
while(i < j)
{
if(a[i] + a[j] == M)
{
printf("%d %d\n",i,j);//打出合适方案
i++;
j--;
}else if(a[i] + a[j] < M)
{
i++;
}else
{
j--;
}
}
例二
合并问题
假设有两个递增的序列a,b.要求将它们合并为一个递增序列c.
同样的,可以设置两个下标i 和 j.初值均设为0。表示分别指向序列a,b的第一个元素。然后根据a[i] 和 b[j]的大小来判断把那个放入c中。
1.如果a[i]<b[j],我们就把a[i]放进去,并且执行i++;b[j] < a[i]时也是一样的。
2.如果两个相等的话随便放进一个,并且只递增放进去的数组的下标,另一个在下一回合会被放进去。当然你也可以两个同时放,同时递增。
3.当一个数组被放完后,另一个剩下的元素一次放入C中。
int merge(int A[],int B[],int C[], int n, int m)
{
int i = 0 ;
int j = 0;
int k =0;
// 当短的那个到达顶点后,另一个剩下的全部全部加上
while(i<n && j<m)
{
if(A[i] == B[j])
{
C[k++] = A[i++];
C[k++] = B[j++];
}
else if(A[i] < B[j])
{
C[k++] = A[i++];
}
else if(B[j] < A[i])
{
C[k++] = B[j++];
}
}
while(i<n)
{
C[k++] = A[i++];
}
while( j < m)
{
C[k++] = B[j++];
}
return k;//返回C的长度
}