題目描述:
輸入一個遞增排序的數組和一個數字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集合中即可。
心靈雞湯:大腦的知識儲備從來不是空穴來風,既然不是天才,爲何不去努力?