题目描述:
输入一个递增排序的数组和一个数字S,在数组中查找两个数,使得它们的和正好是S,如果有多对数字的和等于S,输出两个数的乘积最小的那一对。
思路分析:
由于是递增排序的数组,所以,我们可以使用两个指针,分别指向数组的首元素和尾元素(一个从前往后,指向较小的数,一个从后往前,指向较大的数),这样找到的两个数的乘积必然是最小的。当两个数的和大于S时,较大的数字指针向左移动,当两个数的和小于S时,较小的数字指针向右移动,当两个指针相遇时,查找结束。
代码实现:
import java.util.ArrayList;
public class Exercise20 {
public static void main(String[] args) {
int[] array = {1,2,3,4,6,8,9,12};
System.out.println(findNumbersWithSum(array,12)); //输出结果 [3,9]
}
private static ArrayList<Integer> findNumbersWithSum(int[] array, int sum) {
ArrayList<Integer> list = new ArrayList<>();
//如果原数组的长度小于2或者原数组为空,直接返回list
if(array.length < 2 || array == null){
return list;
}
//定义两个指针,分别指向数组的首元素和尾元素
int low = 0;
int high = array.length - 1;
//对数组进行遍历,当首尾指针相遇时结束,即low = high
//让两个数字的位置不断靠近
while(low < high){
if((array[low] + array[high] > sum)){
//如果首尾元素的和大于sum,则尾指针减一,即 high--
high--;
}else if((array[low] + array[high] < sum)){
//如果首尾元素的和小于sum,则首指针加一,即 low++
low++;
}else{
//如果首尾元素的和等于sum,则把首尾元素加入到list集合中,最后返回即可
list.add(array[low]);
list.add(array[high]);
return list;
}
}
return list;
}
}
解法2代码实现及详细注释:
import java.util.ArrayList;
public class Exercise20 {
public static void main(String[] args) {
int[] array = {-2,-1,0,1,3,4};
System.out.println(findNumbersWithSum1(array,1)); //输出结果 [-2,3]
}
private static ArrayList<Integer> findNumbersWithSum1(int[] array, int sum) {
ArrayList<Integer> list = new ArrayList<>();
//如果原数组的长度小于2或者原数组为空,直接返回list
if(array.length < 2 || array == null){
return list;
}
/**
* 定义两个指针和i和j分别指向数组的第一个元素和第二个元素;
* 外层循环控制第一个元素的位置,内层循环控制第二个元素的位置;
* 如果 array[i] + array[j] = sum 并且list为空,代表其是第一次放入,
* 将p1和p2对应的值添加到list集合中,并且保存乘积max;
* 如果 array[i] + array[j] = sum 并且list集合不为空,代表其不是第一次放入,
* 比较当前的两个值和之前存入的两个值乘积的大小,
* 如果当前乘积小于之前保存的乘积,则重新创建一个list对象
* 保存当前的两个值,并使max = 当前两个值的乘积
* 最后返回list集合即可。
*/
//max用来保存最大的乘积
int max = 0;
//i的取值范围为[0,array.length - 2],i不能指向数组的最后一个元素
for (int i = 0; i < array.length - 1; i++) {
//j的取值范围为[1,array.length - 1],j不能指向数组的第一个元素
for (int j = 1; j < array.length; j++) {
//如果两者相加等于sum,并且list为空,那么代表其是第一次放入
if((array[i] + array[j]) == sum && list.size() == 0){
list.add(array[i]);
list.add(array[j]);
//保存当前两者的乘积
max = array[i] * array[j];
}
//如果两者相加等于sum,并且list不为空,那么代表其不是第一次放入
if((array[i] + array[j]) == sum && list.size() != 0){
//比较当前两个数的乘积与第一次放入的两个数乘积的大小
if((array[i] * array[j]) < max){
/**
* 如果当前i和j指针指向的两个数的乘积小于max,重新new ArrayList一个对象
* 将其放入到list集合中,保存最大值即可
*/
list = new ArrayList<>();
list.add(array[i]);
list.add(array[j]);
//保存当前两者的乘积
max = array[i] * array[j];
}
}
}
}
return list;
}
}
题目总结:
前者解法较为灵活,巧妙的抓住了两个数乘积最小的条件,定义首尾指针遍历时第一次找到的两个数即为所求数。
例:[1,2,3,4] sum = 5 ,1*4肯定小于2*3。
后者解法较为复杂,但易于理解,把第一对元素之和等于sum的两个数的乘积保存下来,与后面出现的每一对元素之和等于sum的两个数的乘积作比较,将较小乘积的那两个数放入到list集合中即可。
心灵鸡汤:大脑的知识储备从来不是空穴来风,既然不是天才,为何不去努力?

在递增排序数组中,通过双指针法寻找和为S的两个数字,使得它们的乘积最小。从数组首尾开始,如果两数之和大于S则减小较大数,反之增加较小数,直至找到满足条件的数对。
313

被折叠的 条评论
为什么被折叠?



