题目描述
输入一个递增排序的数组和一个数字S,在数组中查找两个数,使得他们的和正好是S,如果有多对数字的和等于S,输出两个数的乘积最小的。
输出描述:
对应每个测试案例,输出两个数,小的先输出。
在写解法之前,先思考一个问题,在存在多组和S的情况下,满足什么条件,输出两个数的乘积最小,结论是:在数组中,两个数字离的越远,其乘积就越小。
证明:在存在多组和为S的情况,我们可以做如下假设
x+y=S(S是个常数)
y>x
令y=x+d
=>x+(x+d)=S,
=>x=(S-d)/2
最后推出的x * y的结果为:
x * y = x * (x + d) = (S^2 - d^2) / 4
故当d越大时,其乘积就会越小,所以顺序遍历,遇到的第一组即是我们所要找的结果。
解法一:
顺序遍历数组,令second_num = S - array[i],然后利用二分搜索法(利用泛型算法find也可以),查找second_num是否在数组中。如果在,array[i] 和 second_sum就是最终结果。如果不在,不存在满足条件的数对。
class Solution {
public:
std::vector<int> FindNumbersWithSum(std::vector<int> array, int sum) {
std::vector<int> vec;
if (array.size() <= 1 || array.back() * 2 <= sum || array.front() * 2 >= sum)
return vec;
int first_num, second_num;
int length = array.size() - 1;
int index;
for (int index = 0; index <= length; index++)
{
first_num = array[index];
second_num = sum - first_num;
if (first_num >= second_num)
break;
int i = binarySearch(array, index, length, second_num);
if (i != -1)
{
vec.push_back(first_num);
vec.push_back(second_num);
return vec;
}
}
return vec;
}
private:
int binarySearch(std::vector<int> &array, int start, int end, int key_value)
{
int mid = start + (end - start) / 2;
while (start <= end)
{
if (array[mid] < key_value)
{
start = mid + 1;
}
else if (array[mid] > key_value)
{
end = mid - 1;
}
else
{
return mid;
}
mid = start + (end - start) / 2;
}
return -1;
}
};
解法二:
双指针法,设头尾两个指针 i 和 j
- 如果 i + j == S, 这就是答案;
- 如果 i + j > S, 那么 j 肯定不是我们要找的答案(已经得出 i 前边的数是不可能的), j--;
- 如果 i + j < S, 那么 i 肯定不是我们要找的答案(已经得出 j 后边的数是不可能的), i--;
仅需一次遍历,算法复杂度O(n)
class Solution {
public:
vector<int> FindNumbersWithSum(vector<int> array,int sum) {
vector<int> vec;
if (array.size() <= 1 || array.back() * 2 <= sum || array.front() * 2 >= sum)
return vec;
int i = 0, j = array.size() - 1;
while (i < j)
{
if (array[i] + array[j] == sum)
{
vec.push_back(array[i]);
vec.push_back(array[j]);
return vec;
}
while (i < j && array[i] + array[j] > sum) j--;
while (i < j && array[i] + array[j] < sum) i++;
}
return vec;
}
};