输入一个递增排序的数组和一个数字S,在数组中查找两个数,使得他们的和正好是S,如果有多对数字的和等于S,输出两个数的乘积最小的。(对应每个测试案例,输出两个数,小的先输出。)
- 对于 i + j = sum,当i和j大小越悬殊的的时候 i*j越小,如 i = 1,j=sum-1。
我们可以根据上面的规律,从数组的两头开始寻找。
首尾相加法
- 用i,j两个指针,分别指向首尾
- i从首开始遍历,j从尾开始遍历
- 对于每个i,j都需要全部寻找一遍
public ArrayList<Integer> FindNumbersWithSum(int [] array,int sum) {
ArrayList<Integer> list = new ArrayList<>();
for(int i=0;i<array.length;i++){
for(int j=array.length-1;j>=0;j--){
if(array[i] + array[j] == sum){
list.add(array[i]);
list.add(array[j]);
return list;
}
}
}
return list;
}
这里面存在一个问题,如果a[i] + a[j] < sum
,j依然要 j–,并继续遍历,那么接下来的运算没有意义了。时间复杂度O(n2)
首尾逼近法
这是对上一种方法的改进,核心思想还是从首尾开始找,改变的是通过判断a[i] + a[j]
和sum
的大小关系决定是i
改变还是j
改变。类似于剪纸的思想。
- 数列满足递增,设两个头尾两个指针i和j,
- 若
ai + aj == sum
,就是答案(相差越远乘积越小) - 若
ai + aj > sum
,aj肯定不是答案之一(前面已得出 i 前面的数已是不可能),j --
- 若ai + aj < sum,ai肯定不是答案之一(前面已得出 j 后面的数已是不可能),
i ++
public ArrayList<Integer> FindNumbersWithSum(int [] array,int sum) {
ArrayList<Integer> list = new ArrayList<>();
int i = 0, j = array.length -1;
while(j >= i){
int temp = array[i] + array[j];
if(temp == sum){
list.add(array[i]);
list.add(array[j]);
break;
}else if(temp > sum){
j--;
}else {
i ++;
}
}
return list;
}
时间复杂度O(n)