剑指offer-面试题41:和为s的两个数字VS和为s的连续正数序列

题目一:输入一个递增排序的数组和一个数字s,在数组中查找两个数,使得它们的和正好是s。如果有多对数字的和等于s,输出任意一对即可。例如输入数组{1,2,4,7,11,15}和数字15.由于4+11 = 15,因此输出4和11。

思路:如果固定一个数,再判断其与其他n-1个数的和是不是要求的和,时间复杂度是O(n^2)。很明显不是好的做法,而且注意到数组是已排序的,忽略了这个条件。既然是两个数的和,那么先指定两个数,用指针分别指向数组首元素和尾元素。如果和大于我们要求的sum,说明我们要减小这两个数中的一个,那么可以将后面的指针向前移动。如果小于和sum,则将前面的指针向后移动,直到等于sum。这种指针移动方向的相对的方法可以做到不重复不遗漏。

bool FindNumbersWithSum(int* a, int length, int sum)
{
    if(a == NULL || length <= 0)
        return false;
        
    int* num1 = a;
    int* num2 = a + length - 1;
    while(num1 != num2)
    {
        if(*num1 + *num2 == sum)
        {
            cout << *num1 << " " << *num2 << endl;
            return true;
        }
        else if(*num1 + *num2 < sum)
            ++num1;
        else
            --num2;
    }
    
    return false;
}
题目二:输入一个正数s,打印出所有和为s的连续正数序列(至少含两个数)。例如输入15,由于1+2+3+4+5 = 4+5+6 = 7+8=15,所以结果打印出3个连续序列1~5,4~6和7~8。

思路:和上一题类似,这道题也可以用两个指针的办法,每次求两个指针之间的正数和。指针初试化samll =1和big =2,注意到可能有多个结果,例如输入15当发现1~5这个区间后,整体区间应该后移,所以当我们发现一个合适的答案时应该将big后移。程序终止的条件是什么呢?注意到题目要求至少有两个元素,如果small和big相邻,要求small + big <= sum即small+small+1 <= sum,可以推出small <= (sum-1)/2,注意整数除法会舍去小数造成偏差,因此区间放缩一点点即samll < (sum+1)/2,可以分别将sum取奇数和偶数验证一下。

void FindContinuousSequence(int n)
{
    if(n < 3)
    {
        cout << "Invalid input!" << endl;
        return;
    }
    
    int small = 1;
    int big = 2;
    int sum = small + small;
    while(small < (n+1)/2)
    {
        if(sum == n)
        {
            PrintContinuousSeq(small, big);
            sum += ++big;
        }
        else if(sum < n)
            sum += ++big;
        else
            sum -= small++;
    }
}

void PrintContinuousSeq(int start, int end)
{
    for(int i = start; i <= end; ++i)
        cout << i << " ";
    cout << endl;
}



你可能感兴趣的:(剑指offer-面试题41:和为s的两个数字VS和为s的连续正数序列)