一、two pointers
引例
给定一个递增的正整数序列和一个正整数M,求序列中的两个不同位置的数a和b,使得它们的和恰好为M,输出所有满足条件的方案。
最简单的方法:二重循环枚举
for(int i=0;i<n;i++)
{
for(int j=i+1;j<n;j++)
{
if(a[i]+a[j]==M)
printf("%d %d",a[i],a[j]);
}
}
此时复杂度为O(n2),但由于序列递增,故有以下结论:
1.对一个确定的a[i],若a[i]+a[j]>M,显然后序a[i]+a[j+1]>M,则无需继续遍历
2.若a[i]+a[j]>M,很明显有a[i+1]+a[j]>M成立
我们会发现i与j这两个下标似乎互相牵制,可以给算法提供很大的优化空间
因此,针对这道题目,我们将令i初值为0,j的初值为n-1,即令i,j分别指向序列中第一个和最后一个元素,接下来根据a[i]+a[j]进行下面三种选择:
(1)如果a[i]+a[j] == M;说明已经找到了一组方案,由于序列递增,不等式a[i+1]+a[j]和a[i]+a[j+1]>M均成立,接下来要做的就是将i递增为i+1,j递减以寻找下一组满足条件的方案。
(2)如果a[i]+a[j]>M; 很明显a[i+1]+a[j]也会>M,因此需要对j递减以寻求答案
(3)如果a[i]+a[j]<M;则a[i]+a[j-1]<M,此时应递增i寻求答案
反复执行上述方案,直至i>=j成立,代码如下:
while(i<j){
if(a[i]+a[j]==M)//找到了一组解
{
printf("%d %d",a[i],a[j]);
i++;
j--;
}
else if(a[i]+a[j]<M)
i++;
else
j--;
}
此时复杂度变为O(n),当我们能够充分利用序列递增性质,就可以以浅显的思想降低复杂度。
数组合并
两递增序列A,B合并为一个递增序列C(这个比较基础和简单),只需要设置两个下标i和j,初值均为0,分别指向序列A和B的第一个元素,然后根据A[i]和B[j]的大小决定哪一个放入序列C,直至有一个序列的元素放完,另一个序列的剩余的元素直接加入到序列C即可
int merge(int A[], int B[], int C[],int n, int m){
int i=0, j=0, index = 0;
while(i<n&&j<m){
if(A[i]<=B[j])
C[index++] = A[i++];
else
C[index++] = B[j++]
}
while(i<n) C[index++] = A[i++];
while(j<m) C[index++] = B[j++];
return index;//返回序列C的长度
}
二、打表
用空间换时间的方案,以fibonacci数为例子,在递归的时候记录下已经计算出的答案,例如要求F(8),F(8)=F(6)+F(7);如果没有打表,那么计算F(7)时,F(7)=F(5)+F(6),此时又计算了一次F(6),因此我们可以通过数组记录下F(6)的数值,以便于再次用到F(6)的时候能够在短时间内解决这个问题。