编程之美2.12——快速寻找满足条件的两个数或三个数

来源:岁月联盟 编辑:exp 时间:2012-07-07

问题:
1. 快速找出一个数组中的两个数,让这两个数之和等于一个给定的值。
2. 快速找出一个数组中的三个数,让这三个数之和等于一个给定的值。

1. 解法:算法复杂度为O(nlogn)。先用快速排序对数组排序,让后用双指针(双索引)法对排序好的数组进行反向遍历,并且遍历的方向不变。(若是计算两个数的和,则初始化为i=0,j=n-1,若是计算两个数的差,则初始化为i=0,j=1)
之所以这样遍历方式能成功,是因为排序后,若ai+aj<sum,则ai+ak<sum(k=i,i+1...j),而i之前j之后的情况已遍历过,所以只有i++才可能有等号的情况;若ai+aj>sum,则ak+aj>sum(k=i,i+1...j),而i之前j之后的情况已遍历过,所以只有j--才可能有等号的情况。
[cpp]
#include <iostream> 
#include <algorithm> 
using namespace std; 
 
#define MAXN 1001 
int A[MAXN]; 
 
// 总的时间复杂度为O(nlogn) 
int main() 

    int n, sum, i, j; 
    cin >> n >> sum; 
    for (i=0; i<n; i++) 
        cin >> A[i]; 
    // 快速排序O(nlogn) 
    sort(A, A+n); 
    // 双索引反向遍历 
    i=0; j=n-1;  www.2cto.com
    // 每个数只能用一次(若每个数可以用多次,则用<=) 
    while (i<j) 
    { 
        int plus = A[i]+A[j]; 
        if (plus == sum) 
        { 
            printf("(%d,%d) ",A[i],A[j]); 
            i++, j--; 
        } 
        else if (plus < sum) 
            i++; 
        else 
            j--; 
    } 

2. 解法:时间复杂度为O(n^2)。如果数组已排序,利用解法1的双指针遍历法,可以在O(n)的时间内找到两个数之和等于一个給定的数。我们假设找到的三个数ai,aj,ak有ai<=aj<=ak,要让ai+aj+ak=sum,也就是要ai+aj=sum-ak,设subsum=sum-ak,很容易发现subsum的值只有n个,而确定ai+aj=subsum中的ai,aj只需要O(n)的时间,所以总的时间复杂度为O(nlogn+n*n)=O(n^2)
[cpp] 
#include <iostream> 
#include <algorithm> 
using namespace std; 
 
#define MAXN 1001 
int A[MAXN]; 
 
int main() 

    int n, sum, i, j, k; 
    cin >> n >> sum; 
    for (i=0; i<n; i++) 
        cin >> A[i]; 
    sort(A, A+n); 
    for (k=0; k<n; k++) 
    { 
        i=0; j=k-1; 
        if (k==i) i++; 
        if (k==j) j--; 
        int subsum = sum-A[k]; 
        while (i<j) 
        { 
            int plus = A[i]+A[j]; 
            if (plus == subsum) 
            { 
                printf("(%d,%d,%d) ",A[i],A[j],A[k]); 
                i++, j--; 
            } 
            else if (plus < subsum) 
                i++; 
            else 
                j--; 
            if (k==i) i++; 
            if (k==j) j--; 
        } 
    } 

若允许每个数被多次选取,只需稍微改下代码,具体如下:
[cpp] 
#include <iostream> 
#include <algorithm> 
using namespace std; 
 
#define MAXN 1001 
int A[MAXN]; 
 
int main() 

    int n, sum, i, j, k; 
    cin >> n >> sum; 
    for (i=0; i<n; i++) 
        cin >> A[i]; 
    sort(A, A+n); 
    for (k=0; k<n; k++) 
    { 
        i=0; j=k; 
        //if (k==i) i++; 
        //if (k==j) j--; 
        int subsum = sum-A[k]; 
        while (i<=j) 
        { 
            int plus = A[i]+A[j]; 
            if (plus == subsum) 
            { 
                printf("(%d,%d,%d) ",A[i],A[j],A[k]); 
                i++, j--; 
            } 
            else if (plus < subsum) 
                i++; 
            else 
                j--; 
            //if (k==i) i++; 
            //if (k==j) j--; 
        } 
    } 


作者:linyunzju