- 输入一个递增排序的数组和一个数字S,在数组中查找两个数,使得他们的和正好是S,如果有多对数字的和等于S,输出两个数的乘积最小的。
对应每个测试案例,输出两个数,小的先输出。
下面的思路来自《剑指offer》,在《41.和为S的连续正数序列》中已经介绍过解法。
-
最容易想到的复杂度为$O(n^2)$的算法为,首先固定一个数,然后把剩下的所有数字和它相加。但这种方法效率太低了,因此想到另外一种方法,由于数组是排序的,因此可以设置两个指针,分别指向数组的头(
small
)和尾(big
),当两个指针指向数字之和小于S
的时候,将small
指针向后移动,如果两个数字之和大于S
的话就把big
指针向前移动,最终一定能找到这对数字,复杂度为$O(n)$。 -
外层的两个数的乘积更小,证明如下:
假设
a,b
是内层的两个数,且b>a, a+b=S
,同时存在外层的两个数(a-m)+(b+m)=S, m>0
,那么就有(a-m)*(b+m)=ab-(b-a)*m-m^2<ab
,因此外层的乘积比内层小,输出外层的两个数即可。也可以这样证明,假设
a,b
是外层的两个数,且b>a, a+b=S
,同时存在内层的两个数(a+m)+(b-m)=S, m>0
,那么就有(a+m)*(b-m)=ab+(b-a)*m-m^2
,由于数组是递增的,因此b-a>m
,所以(a+m)*(b-m)>ab
,因此内层的乘积更大。
- C++
class Solution {
public:
vector<int> FindNumbersWithSum(vector<int> array,int sum) {
vector<int> result;
int length = array.size();
if(length<2) return result;
int pSmall = 0, pBig = length-1;
while(pSmall < pBig){
int curSum = array[pSmall] + array[pBig];
if(curSum > sum) pBig--;
else if(curSum < sum) pSmall++;
else{
result.push_back(array[pSmall]);
result.push_back(array[pBig]);
break;
}
}
return result;
}
};