题目:
输入一个递增排序的数组和一个数字S,在数组中查找两个数,使得他们的和正好是S,如果有多对数字的和等于S,输出两个数的乘积最小的。
输入示例:
[1, 3, 4, 5, 6, 9], 8
输出示例:
[3, 5]
规定:
①需要注意如果有多对数字的和等于S,那么输出两个数的乘积最小的。
②对应每个测试案例,输出两个数,小的先输出。
解题思路:
思路:读题,注意数组是递增排序,很容易想到的就是二分查找和头尾双指针的解法。初步估计解决该问题时,二分查找时间复杂度是O(NlogN),头尾双指针法是O(N)。
注:O(NlogN)是因为此处的二分查找需要对每个元素进行,判断某元素与数组其他元素之和是否为目标值,即N * logN。
(1)二分查找方法
对于数组的每一个元素,挑出来,然后在剩下的所有元素中做二分查找,找到是否有数与之和为目标值。如果有则试图存进结果数组,另外如果结果数组中有值还需要对乘积大小进行判断,将小乘积的两个数放进去。
(注:可能第一时间想到这种方法,方法其实比较笨,时间复杂度O(NlogN)比较高,不推荐,但是可以作为引子,给自己面试时留优化的空间。)
class Solution {
public:
void swap(vector<int>&array, int i, int j){
//交换数组元素的函数
int temp = array[i];
array[i] = array[j];
array[j] = temp;
}
int binary_search(vector<int>&array, int low, int high, int diff){
//二分搜索
if(diff<array[low]||diff>array[high]){
return -1;
}
auto mid = (low+high)/2;//auto自动数据类型推导
if(diff<array[mid]){
return binary_search(array,low,mid-1,diff);
}
else if(diff>array[mid]){
return binary_search(array,mid+1,high,diff);
}
else{
return mid;
}
}
vector<int> FindNumbersWithSum(vector<int> array,int sum) {
//复杂度O(NlogN)
vector<int> result;
int length = array.size();//长度
if(length<=1)
return result;
for(int low=0;low<length;low++){
swap(array, low, 0);
int index = binary_search(array,1,length-1,sum-array[0]);
if(index!=-1){
if(result.empty()){
result.push_back(array[0]);//注意此处由于swap后锁定数的下标是0
result.push_back(array[index]);
}
else{
int last1=result.front();
int last2=result.back();
if(array[0]*array[index]<last1*last2){
result.clear();
result.push_back(array[0]);
result.push_back(array[index]);
}
}
}
swap(array, low, 0);
}
return result;
}
};
(2)头尾双指针方法
对于有序数组,考虑用头尾双指针遍历法解决,头指针从头部向后,尾指针从尾部向前。其中如果两指针指向元素和等于目标值,则试图存进结果数组。如果大于目标值,则尾指针减小。如果小于目标值,则头指针增加。例如[1,2,4,6,9],8
,最开始有1+9>8
,说明需要将尾指针减小,减小后1+6<8
,说明需要将头指针增大,增大后2+6=8
,则将2
和6
存进结果数组。另外注意如果结果数组中有值还需要对乘积大小进行判断,将小乘积的两个数放进去。
class Solution {
public:
int sum_equal(vector<int>&array, int low, int high, int sum){
//比对两个指针指向的元素和是否等于sum
if(sum<array[low]){
return -1;
}
int dsum = array[low]+array[high];//两数之和
if(dsum==sum){
return 1;
}
else if(dsum<sum){//如果相加值比目标数小
return 0;//增大low
}
else{//如果相加值比目标数大
return 2;//减小high
}
}
vector<int> FindNumbersWithSum(vector<int> array,int sum) {
//复杂度O(N)
vector<int> result;
int length = array.size();//长度
if(length<=1)
return result;
for(int low=0,high=length-1;low<high;){
int return_flag = sum_equal(array,low,high,sum);
if(return_flag==-1){
return result;
}
if(return_flag==1){
if(result.empty()){
result.push_back(array[low]);
result.push_back(array[high]);
}
else{
int last1=result.front();
int last2=result.back();
if(array[low]*array[high]<last1*last2){
result.clear();
result.push_back(array[low]);
result.push_back(array[high]);
}
}
low++;
high--;
}
else if(return_flag==0){
low++;
}
else{
high--;
}
}
return result;
}
};
输入输出测试:
测试用例1:#只有一个组合满足
输入:
[1, 2, 5, 6, 8], 8
输出:
[2, 6]
测试用例2:#有多个组合满足,输出乘积最小的
输入:
[1, 2, 5, 6, 7], 8
输出:
[1, 7]