排序数组,找出两个和等于指定数

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

解:如果只是最简单的遍历,时间复杂度为O(n)。这样不符合题意,而且也没有充分的利用题目的已知条件“排好序的数组”。这题目老早之前做过一次。印象中使用了两个指针p、q,一个指针p指向数组头,一个q指向末尾。指针p只往大的方向走;q只往小的方向走;比较这两值之和与指定数的大小关系。若大了,则q往小的方向走;若小了,则p往大的方向走;直到找到一个或是p>q(未找到)。

重新做一遍的时候,我又想到了另外的一个方法(可能别人已经这样用,这思想我残余的记忆中,逐成),当然没有这个好。

先说第一个方法。其实,更主要的我想证明其正确性。虽然以前做过,但没有细究何以其然,秉着数学严谨的精神。花了点时间思考之:

已知:a[1]<=a[2]<=...<=a[n],和数m。求找出两个数a[i]、a[j],使得a[i]+a[j]=m。

证:(1)不妨先假设存在这么两个数的情况。

为了更好证明,将数组写成a[min]<=...<=a[max],min表示当前有序集合的最小数的下标,max表示当前有序集合的最大数的下标。

  • 如果a[min]+a[max]>m,则说明了a[max]+a[i]>m(min<=i<max),即a[max]和其余的任何一个的和都不可能为m,所以这是可以将可以排除a[max],此时,有序集合缩小为{a[min],a[min+1],...,a[max-1]},要求的两个数将在这个集合中;此时重新迭代。
  • 如果a[min]+a[max]<m,则说明了a[min]+a[i]>m(min<i<=max),即a[min]和其余的任何一个的和都不可能为m,所以这是可以将可以排除a[min],此时,有序集合缩小为{a[min-1],a[min],...,a[max]},要求的两个数将在这个集合中;此时重新迭代。
  • 如果a[min]+a[max]=m,找到这两个数。

如果集合存在这两个数,则以上的算法是可以收敛到这两个数。

(2)如果不存在这么两个数。这个容易证明类似以上的算法也是适合的。试想不存在a[i]+a[j]=m (i!=j),这就可能进入到相等的情况。要不max减小,要不min增加。这样集合总会缩小。直到集合为0个元素(为一个元素就可说明找不到)

证毕。

 1 #include <iostream>
 2 
 3 using namespace std;
 4 
 5 int main()
 6 {
 7     int num[] = {1,2,4,7,11,15};
 8     int len = sizeof(num)/sizeof(int);
 9     int n;
10     int *p,*q;
11 
12     cin>>n;
13     p = num;
14     q = &num[len-1];
15 
16     while(p<q && *q>=n)
17     {
18         q--;
19     }
20     while(p<q)
21     {
22         if (*q+*p < n)
23         {
24             p++;
25         }
26         else if (*p + *q >n)
27         {
28             q--;
29         }
30         else
31         {
32             cout<<*p<<" "<<*q<<endl;
33             break;
34         }
35     }
36     if (!(p<q))
37     {
38         cout<<"Not found"<<endl;
39     }
40     
41     return 0;
42 }

第二方法:先计算出每一个数还需要多少才能到达m

如题目中的例子:

数组:1、 2、4、7、11、15

补集:14、13、11、8、4、0

 补集说明若原数组存在这个则可以找到这两个数。逆补集中数:0说明15+0=15,由于数组是有序,顺序遍历原数组1,1>0说明不可能存在0这数;这时,找补集中4,接着上次遍历原数组1,1<4。接着遍历原数组2,2<4。下一个4=4找到,即为11和4。整个顺序遍历是不回溯只走一遍,逆向遍历补集也是只遍历一次(证明略)。所以整个算法复杂度为O(n),空间复杂度为O(n)。其实根据这个思想也可以将空间复杂度降到O(1),留个习题(太简单了O(∩_∩)O,所以代码就不给出了)。

posted @ 2013-03-18 19:13  legendmaner  阅读(604)  评论(0编辑  收藏  举报