42 和为S的两个数字


题目要求:输入一个递增排序的数组和一个数字S,在数组中查找两个数,使得他们的和正好是S,如果有多对数字的和等于S,输出两个数的乘积最小的。
对应每个测试案例,输出两个数,小的先输出。
 
 
 

数列满足递增,设两个头尾两个指针i和j,
 1、若ai + aj == sum,就是答案(相差越远乘积越小)
 2、若ai + aj > sum,aj肯定不是答案之一(前面已得出 i 前面的数已是不可能),j -= 1
 3、若ai + aj < sum,ai肯定不是答案之一(前面已得出 j 后面的数已是不可能),i += 1

      时间复杂度  O(n)

其实主要思想是两个数的乘积要最小,那么这个时候我们就需要知道,当一个有序的序列,两个数相隔越远,最后得到的数最小。所以我们设定两个指针,分别从序列的两头出发。

所以这种思路,第一次找到的答案,就是最小,不同额外考虑其他满足的两个数

有负数也是第一组乘积最小。和一定两个数,其乘积是倒抛物线,越远离对称轴越小,与正负无关

 


找到的第一组(相差最大的)就是乘积最小的。可以这样证明:考虑x+y=C(C是常数),x*y的大小。不妨设y>=x,y-x=d>=0,即y=x+d, 2x+d=C, x=(C-d)/2, x*y=x(x+d)=(C-d)(C+d)/4=(C^2-d^2)/4,也就是x*y是一个关于变量d的二次函数,对称轴是y轴,开口向下。d是>=0的,d越大, x*y也就越小。(https://www.nowcoder.com/questionTerminal/390da4f7a00f44bea7c2f3d19491311b)

 1 import java.util.ArrayList;
 2 public class Solution {
 3     public ArrayList<Integer> FindNumbersWithSum(int [] array,int sum) {
 4         ArrayList<Integer> list = new ArrayList<Integer>();
 5         //考虑特殊情况,这里array.length要考虑是否小于2了,而不是平常的1
 6         if (array == null || array.length < 2) {
 7             return list;
 8         }
 9         //定义最左指针i和最右指针j
10         int i=0,j=array.length-1;
11         while(i<j){
12             if(array[i]+array[j]==sum){
13             list.add(array[i]);//添加的时候,肯定是小数在前了
14             list.add(array[j]);
15                 return list;
16            }else if(array[i]+array[j]>sum){
17                 j--;
18             }else{
19                 i++;
20             }
21         }
22         return list;
23     }
24 }

 

 

posted @ 2019-07-23 09:52  淡如水94  阅读(107)  评论(0编辑  收藏  举报