在排序数组中查找和为给定值的两个数字--总结

在排序数组中查找和为给定值的两个数字--总结

分类: 数据结构与算法   1220人阅读  评论(0)  收藏  举报

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

 

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

 

解法一:因为只需要输出满足要求的一对数字即可,这里可以设置两个指针,分别指向数组头和尾,然后进行判断。假设数组为a,头指针为h,尾指针为t,输入的和为M,则有下面这几种情况:

 

1、若a[h]+a[t]==M, 则a[h]和a[t]即为满足条件的一对数。

2、若a[h]+a[t]< M, 说明应该增加两个数的和,则头指针后移。

3、若a[h]+a[t]> M, 说明应该减小两个数的和,则尾指针前移。

 

这个while循环退出的条件为:找到满足条件的一对数或者h>=t。

 

这种思路也是原文作者的思路,这个题的想法和之前的“和为N连续正数序列”这题的一种解法相似。

我的代码:

[c-sharp]  view plain copy
  1. /* 
  2.  * 若有符合条件的一对数,则存入ret1和ret2中,并且返回0 
  3.  * 否则返回-1 
  4.  * 
  5.  * 注意:函数中没有对传入参数进行检测!! 
  6.  *  
  7. */  
  8. int foo10(int *arr, int len, int m, int *ret1, int *ret2){  
  9.     int h,t;  
  10.     int *a = arr;  
  11.   
  12.     h = 0;  
  13.     t = len-1;  
  14.   
  15.     while(h<t){  
  16.         if(a[h]+a[t]==m){  
  17.             *ret1 = a[h];  
  18.             *ret2 = a[t];  
  19.             return 0;  
  20.         }else if(a[h]+a[t]<m)  
  21.             h++;  
  22.         else  
  23.             t--;  
  24.     }  
  25.   
  26.     return -1;  
  27.   
  28. }  

 

解法二:假设有序数组为a,设一个辅助数组b,使得b[i] = M-a[len-1-i],也就是说,使M减去a中的所有元素,并且把这些元素进行升序排序后存入b中。由b[i] = M-a[len-1-i]可得 b[i] + a[len-1-i] = M,那如何把b[i]映射到a中的某个元素呢?我们知道:

b[0] = M - a[len-1]

b[1] = M - a[len-2]

...

b[len-1] = M - a[0]

 

若a[0] = b[0],则 a[0] = M - a[len-1],则 a[0] + a[len-1] = M。所以,可以这么做:

得到b之后,若存在某个i,使 a[i] = b[i],说明存在符合条件的一对数,为a[i],a[len-1-i]。

写了一个测试代码,没有完全的返回值和参数检测,只是说明这种思路吧:

[c-sharp]  view plain copy
  1. int foo10_v2(int *arr, int len, int m){  
  2.     int i;  
  3.     int *a = arr;  
  4.   
  5.     for(i=0;i<len;++i){  
  6.         if(m-a[len-1-i]==a[i])  
  7.             printf("[debug]ret1=%d,/tret2=%d/n",a[i],a[len-1-i]);  
  8.     }  
  9.   
  10.     return 0;  
  11. }  

 

下面引用原文内容,更好地说明第一种解法:

 

================= 以下内容引自原文 =======================

 

分析:如果我们不考虑时间复杂度,最简单想法的莫过去先在数组中固定一个数字,再依次判断数组中剩下的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;
}

扩展:如果输入的数组是没有排序的,但知道里面数字的范围,其他条件不变,如和在O(n)时间里找到这两个数字?

 

原文地址:http://zhedahht.blog.163.com/blog/static/2541117420072143251809/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值