排序数组中和为给定值的两个数字

题目:输入一个已经按升序排序过的数组和一个数字,在数组中查找两个数,使得它们的和正好是输入的那个数字。要求时间复杂度是O(n)。如果有多对数字的和等于输入的数字,输出任意一对即可。

例如输入数组12471115和数字15。由于4+11=15,因此输出411

分析:如果我们不考虑时间复杂度,最简单想法的莫过去先在数组中固定一个数字,再依次判断数组中剩下的n-1个数字与它的和是不是等于输入的数字。可惜这种思路需要的时间复杂度是O(n2)

我们假设现在随便在数组中找到两个数。如果它们的和等于输入的数字,那太好了,我们找到了要找的两个数字;如果小于输入的数字呢?我们希望两个数字的和再大一点。由于数组已经排好序了,我们是不是可以把较小的数字的往后面移动一个数字?因为排在后面的数字要大一些,那么两个数字的和也要大一些,就有可能等于输入的数字了;同样,当两个数字的和大于输入的数字的时候,我们把较大的数字往前移动,因为排在数组前面的数字要小一些,它们的和就有可能等于输入的数字了。

我们把前面的思路整理一下:最初我们找到数组的第一个数字和最后一个数字。当两个数字的和大于输入的数字时,把较大的数字往前移动;当两个数字的和小于数字时,把较小的数字往后移动;当相等时,打完收工。这样扫描的顺序是从数组的两端向数组的中间扫描。

问题是这样的思路是不是正确的呢?这需要严格的数学证明。感兴趣的读者可以自行证明一下。

参考代码:

///
 // Find two numbers with a sum in a sorted array
 // Output: ture is found such two numbers, otherwise false
 ///
 bool FindTwoNumbersWithSum
 (
       int data[],           // a sorted array
       unsigned int length,  // the length of the sorted array     
       int sum,              // the sum
       int& num1,            // the first number, output
       int& num2             // the second number, output
 )
 {
 
       bool found = false;
       if(length < 1)
             return found;
 
       int ahead = length - 1;
       int behind = 0;
 
       while(ahead > behind)
       {
             long long curSum = data[ahead] + data[behind];
 
             // if the sum of two numbers is equal to the input
             // we have found them
             if(curSum == sum)
             {
                   num1 = data[behind];
                   num2 = data[ahead];
                   found = true;
                   break;
             }
             // if the sum of two numbers is greater than the input
             // decrease the greater number
             else if(curSum > sum)
                   ahead --;
             // if the sum of two numbers is less than the input
             // increase the less number
             else
                   behind ++;
       }
 
       return found;
 }

扩展(1):输入一个数组,判断这个数组中是不是存在三个数字i, j, k,满足i+j+k等于0。在我的英文博客http://codercareer.blogspot.com/2011/10/no-09-numbers-with-given-sum.html里详细讨论了这个题目。

扩展(2):如果输入的数组是没有排序的,但知道里面数字的范围,其他条件不变,如何在O(n)时间里找到这两个数字?这个的基本思路是先用哈希表实现O(n)的排序(请参照本面试题系列的第57题),接下来的步骤都一样了。

以上转自何海涛博客

扩展(1)的参考地址需要翻墙,在此一并转载

No. 09 - Numbers with a Given Sum

 

Problem 1: Given an increasingly sorted array and a number s, please find two numbers whose sum is s. If there are multiple pairs with sum s, just output any one of them.
 
For example, if the inputs are an array {1, 2, 4, 7, 11, 15} and a number 15, please out two numbers 4 and 11 since 4+11=15.
 
Analysis:  Let us firstly have a try to select two numbers (denoted as num1 and num2) of the input array. If their sum equals to s, we are fortunate because the required numbers have been found. If the sum is less than s, we may replace num1 with its next number, because the array is increasingly sorted and its next number should be greater. If the sum is greater than s, num2 can be replaced with its previous number in the sorted array, which should be less than num2.
 
Take the array {1, 2, 4, 7, 11, 15} and number 15 as an example. We define two pointers. At the first step, they point to the first number (also the least one) 1 and the last number (also the greatest one) 15. We move the second pointer forward to number 11, since their sum 16 and it is greater than 15.
 
At the second step, the two numbers are 1 and 11, and their sum 12 is less than 15. Therefore, we can move the first pointer afterward and let it point to 2.
 
The two numbers are 2 and 11 accordingly at the third step. Since their sum 13 is less than 15, we move the first pointer afterward again.
 
Now the two numbers are 4 and 11, and their sum is 15 which is the expected sum. Therefore, we have found two numbers whose sum is 15.
 
The process is summarized as in Table 1:
 
Step
Num1
Num2
Sum
Comparing with s
Operation
1
1
15
16
Greater
Select the previous number of Num2
2
1
11
12
Less
Select the next number of Num1
3
2
11
13
Less
Select the next number of Num1
4
4
11
15
Equal
 
Table 1: Find a pair of numbers with sum 15 out of array {1、2、4、7、11、15}
 
After interviewers approve our solution, we can begin to write code. The following is a piece of sample code:
bool FindNumbersWithSum(int data[], int length, int sum, 
                         int* num1, int* num2)
 {
     bool found = false;
     if(length < 1 || num1 == NULL || num2 == NULL)
         return found;
 
 
     int ahead = length - 1;
     int behind = 0;
 
 
     while(ahead > behind)
     {
         long long curSum = data[ahead] + data[behind];
 
 
         if(curSum == sum)
         {
             *num1 = data[behind];
             *num2 = data[ahead];
             found = true;
             break;
         }
         else if(curSum > sum)
             ahead --;
         else
             behind ++;
     }
 
 
     return found;
 }

In the code above, ahead is the index of the first number, and behind is the index of the second number. Since we only need to scan the input array once, its time complexity is O(n).
 
Problem 2:  Given an array, please determine whether it contains three numbers whose sum equals to 0.
 
Analysis: This problem is also required to find some numbers with a given array and sum, it is similar to the first problem. We may get some hints from the solution above.
 
Since the input of the previous problem is an increasingly sorted array, we can also firstly sort the input array increasingly. Secondly we scan the array. When we reach the i-th number with value n, we try to find whether there are two numbers whose sum is –n in the array excluding the i-th number.
 
It is time for us to write some code. We modify the function FindNumbersWithSum above a little bit:
bool FindNumbersWithSum(int data[], int length, int sum, int excludeIndex)
 {
     bool found = false;
     int ahead = length - 1;
     int behind = 0;
 
 
     while(ahead > behind)
     {
         if(ahead == excludeIndex)
             ahead --;
         if(behind == excludeIndex)
             behind ++;
 
 
         long long curSum = data[ahead] + data[behind];
 
 
         if(curSum == sum)
         {
             found = true;
             break;
         }
         else if(curSum > sum)
             ahead --;
         else
             behind ++;
     }
 
 
     return found;
 }
It determines whether there are two numbers whose sum is sum in array data excluding the number with index excludeIndex. We can determine whether there are three numbers in an array with sum 0 based on this function:
bool HasThreeNumbersWithSum0(int data[], int length)
 {
     bool found = false;
     if(data == NULL || length < 3)
         return found;
 
 
     std::sort(data, data + length - 1);
 
 
     for(int i = 0; i < length; ++i)
     {
         int sum = -data[i];
         int num1, num2;
         found = FindNumbersWithSum(data, length, sum, i);
 
 
         if(found)
             break;
     }
 
 
     return found;
 }


It contains two steps in function HasThreeNumbersWithSum0. It costs O(n logn)time to sort n numbers at its first step. At its second step it costs O(n) time for each number to call FindNumberWithSum, so it costs O(n 2) time in the for loop. Therefore, its overall time complexity is O(n 2).

The author Harry He owns all the rights of this post. If you are going to use part of or the whole of this ariticle in your blog or webpages, please add a reference to   http://codercareer.blogspot.com/. If you are going to use it in your books, please contact me (zhedahht@gmail.com) . Thanks.



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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值