寻找和为定值的两个数

题目

输入一个数组和一个数字,在数组中查找两个数,使得它们的和正好是输入的那个数字。

要求时间复杂度是O(N)。如果有多对数字的和等于输入的数字,输出任意一对即可。

例如输入数组1、2、4、7、11、15和数字15。由于4+11=15,因此输出4和11。

暴力法

直接穷举,从数组中任意选取两个数,判定它们的和是否为输入的那个数字。此举复杂度为O(N^2)。

#include<iostream>
using namespace std;

int array[] = {13, 8, 90, 7, 6, 9, 11, 5, 10};
int length = sizeof(array)/sizeof(array[0]);
const int value = 15;

void TheSumValue(int *p, int length){
    if(NULL == p || length < 2)
        return ;
    for(int i=0; i<length; i++)
        for(int j=0; j<length; j++){
            if(i == j)
                continue;
            if(p[i] + p[j] == value){
                cout << p[i] << " + " << p[j] << " = " << value << endl;
                i = length;
                j = length;
                break;
            }
        }
}

int main(){
    TheSumValue(array, length);
    return 0;
}

很显然,我们要寻找效率更高的解法

相减法

题目相当于,对每个a[i],查找sum-a[i]是否也在原始序列中,每一次要查找的时间都要花费为O(N),这样下来,最终找到两个数还是需要O(N^2)的复杂度。

void TheSumValue(int *p, int length){
    if(NULL == p || length < 2)
        return ;
    int *temp = new int[length];
    for(int i=0; i<length; i++){
        int diff = value - p[i];
        for(int j=0; j<length; j++){
            if(i == j)
                continue;
            if(diff == p[j]){
                cout << p[i] << " + " << diff << " = " << value << endl;
                i = length;
                j = length;
            }
        }
    }
}

那如何提高查找判断的速度呢?

相减法之二分查找

从上面我们可以直到,sum-a[i]在查找自己是否在原数组中时,是一个一个的判断,这样比较耗时间,那么如果是先将已有数组进行排序,排序的复杂度为O(n * logn), 排序过后再使用二分法查找,二分法查找的复杂度为O(logn),这样整体的复杂度就为O(n * logn)。

#include<iostream>
using namespace std;

int array[] = {13, 8, 90, 7, 6, 9, 11, 5, 10};
const int length = sizeof(array)/sizeof(array[0]);
const int value = 15;

void printArray(int a[], int length){
    if(NULL == a || length < 0)
        return ;
    for(int i=0; i<length; i++)
        cout << a[i] << " ";
    cout << endl;
}

//sort the array from small to large, use the heap method
void HeapAdjust(int (&a)[length], int pos, int len){
    if(NULL == a || len < 2 || pos > len || pos < 0)
        return ;
    int child = 2*pos + 1;
    int temp = a[pos];
    while(child < len){
        if((child+1)<len && a[child+1]>a[child])
            child++;
        if(a[pos] < a[child]){
            a[pos] = a[child];
            pos = child;
            child = 2*pos + 1;
        } else {
            break;
        }
        a[pos] = temp;
    }
}

void BuildingHeap(int (&b)[length], int len){
    if(NULL == b || len < 2)
        return ;
    for(int i=(len-1)/2; i>=0; i--){
        HeapAdjust(b, i, len);
    }
}

void HeapSort(int (&c)[length], int len){
    if(NULL == c || len < 2)
        return ;
    BuildingHeap(c, len);
    for(int i=len-1; i>=0; --i){
        int temp = c[i];
        c[i] = c[0];
        c[0] = temp;
        HeapAdjust(c, 0, i);
    }
}

int BinarySearch(int a[], int searchValue, int len){
    int front = 0;
    int end = len-1;
    int pos = (front + end)/2;
    while(pos >=0 && pos < len){
        if(searchValue == a[pos])
            return pos;
        else if(searchValue < a[pos]){
            end = pos;
            pos = (front + end)/2;
        }
        else{
            front = pos;
            pos = (front + end)/2;
        }
    }
    return -1;
}

void TheSumValue(int *p, int len){
    int diff=0;
    for(int i=0; i<len; i++){
        diff = value - p[i];
        int signal = BinarySearch(p, diff, len);

        if(signal < 0)
            continue;
        else{
            cout << p[i] << " + " << p[signal] << " = " << value << endl;
            break;
        }
    }
}

int main(){
    printArray(array, length);
    BuildingHeap(array, length);
    printArray(array, length);
    HeapSort(array, length);
    printArray(array, length);

    cout << "the sum value is " << value << " : ";
    TheSumValue(array, length);
    return 0;

    return 0;
}

由程序我们可以了解到,主要的时间复杂度实在排序和查找上,花费为O(n * logn), 而空间复杂度为O(1), 那么我们可不可以用空间复杂度来换取时间复杂度呢?

相减法之空间换时间

第一个数组以一指针i 从数组最左端开始向右扫描,第二个数组以一指针j 从数组最右端开始向左扫描,如果下面出现了和上面一样的数,即a[*i]=a[*j],就找出这俩个数来了。如上,i,j最终在第一个,和第二个序列中找到了相同的数4和11,,所以符合条件的两个数,即为4+11=15。怎么样,两端同时查找,时间复杂度瞬间缩短到了O(N),但却同时需要O(N)的空间存储第二个数组(要达到O(N)的复杂度,第一个数组以一指针i 从数组最左端开始向右扫描,第二个数组以一指针j 从数组最右端开始向左扫描,首先初始i指向元素1,j指向元素0,谁指的元素小,谁先移动,由于1(i)>0(j),所以i不动,j向左移动。然后j移动到元素4发现大于元素1,故而停止移动j,开始移动i,直到i指向4,这时,i指向的元素与j指向的元素相等,故而判断4是满足条件的第一个数;然后同时移动i,j再进行判断,直到它们到达边界)。
http://blog.csdn.net/v_JULY_v/article/details/6419466

#include<iostream>
using namespace std;

int array[] = {1, 3, 7, 9, 12, 15, 17, 20};
const int length = sizeof(array)/sizeof(array[0]);
const int value = 15;

int diff[length];

void TheSumValue(int *p, int len){
    for(int i=0; i<len; i++)
        diff[i] = value - *(p+i);
    int* front = p;
    int* rear = diff + len - 1;

    /*进行判断的时候首先要判断front和rear的值的大小,值小的指针继续移动,值大的指针保持不动,这样一支比下去,若遇到值相等的情况,那么就肯定存在值相加等于某个固定值的情况但是这种方法只适用于已经排好序的情况*/ 
    while(front-p < len && rear-diff >=0){
        if(*front > *rear)
            rear--;
        else if(*front < *rear)
            front++;
        else{
            cout << *front << " + " << value-*rear << " = " << value << endl;
            break;
        }

     }
}

int main(){
    TheSumValue(array, length);

    return 0;
}

时间空间最优解法

在上一个解法中,假设数组是有序排列的(若数组不是有序排列的,则使用快速排序或者堆排序,时间复杂度为O(nlogn),那么时间复杂度为O(n),空间复杂度为O(n),那么我们是否可以在保证时间复杂度的情况下,降低空间复杂度呢?答案是可以的。

使用首尾指针front指向数组头,rear指向数组尾,然后判断*front与 *rear之和是否等于sum:

  1. 若*front + *rear > sum, 那么就需要让整体相加之和更小一些,因此rear指针需要左移;
  2. 若*front + *rear < sum, 那么就需要让整体相加之和更大一些,因此front指针需要右移;
#include<iostream>
using namespace std;

int array[] = {1, 4, 6, 7, 9, 10, 12, 14, 15};
const int length = sizeof(array)/sizeof(array[0]);
const int value = 15;

void TheSumValue(int *p, int len){
    if(NULL == p || len < 2)
        return ;
    int *front = p;
    int *rear = p + len - 1;
    while(front < rear){
        if(*front + *rear >  value)
            --rear;
        else if(*front + *rear < value)
            ++front;
        else{
            cout << *front << " + " << *rear << " = " << value << endl;
            break;  
        }
    }
}

int main(){
    TheSumValue(array, length);

    return 0;
}

总结

解这类题目其实是有迹可循的,首先我们需要确定数组是否有序,若有序则可以直接进行两个数的查找,使用最后一种方法最好,若无序,则先需要进行排序,排序好了过后再进行查询即可。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值