解
第一步,萬年不變的查錯。如果給的array是null或不夠兩個數,直接return 0
public int twoSumClosest(int[] nums, int target) {
if (nums == null || nums.length < 2) {
return 0;
}
...
}
看題,第一步想到暴力解法,即找到每一對數字的和,再從這一堆和里面,找出與target最接近的(打擂臺)。這種方式為O(n^2),因為每一個數字都要和除自己以外的數字算一次。怎樣進行優化呢?題里有提示,挑戰用O(nlogn)來解。O(nlogn)大部分就是排序,因為排序最低就是O(nlogn)。所以想到對array排序。
Arrays.sort(nums);
然后就是怎么利用一個已經排好序的數列。首先要有一個最低的差,minSumDiff
。初始化為最大值,以為一會要用它來比小,所以第一次不管是什么數字,都不會比它大。再來兩個pointer,一左一右。
int minSumDiff = Integer.MAX_VALUE;
int left = 0;
int right = nums.length - 1;
左右兩個pointer同時開始向中間移動,如果需要數字之和變小,那就右邊往左移。找到當前兩個pointer指向的數字之和sum
,以及跟target的差sumDiff
。
while (left < right) {
int sum = nums[left] + nums[right];
if (sum == target) {
return 0;
}
int sumDiff = Math.abs(target - sum);
...
}
這里有可能出現一個edge case,sum跟target一樣,即它們的差為0。當這個時候,我加了一個if statement
,如果出現就直接返回,既然找到一樣的了,后面就沒有繼續遍歷的必要了。這里加不加都可以。如果數列里出現一個和跟target一樣的,那么加了就比不加好。相反,如果沒有,加了就不如不加。可以問面試官那種可能更大一點。
如果需要數字之和變大,那就左邊往右移。移動的過程中,不斷的進行打擂臺,對比當前最低的接近差,如果比當前最低的低,那就把最低的改成現在的差。
minSumDiff = Math.min(minSumDiff, sumDiff);
然后如果當前的和小于target,那我們就去找下一個大一點的數,即右邊pointer往左移。如果當前的和大于target,那我們就去找下一個小一點的數,即左邊的pointer往右移。
if (sum < target) {
left++;
} else {
right--;
}
最后,返回當前最低的差即可。
return minSumDiff;
完整的code
public class Solution {
/*
* @param nums: an integer array
* @param target: An integer
* @return: the difference between the sum and the target
*/
public int twoSumClosest(int[] nums, int target) {
if (nums == null || nums.length < 2) {
return 0;
}
Arrays.sort(nums);
int minSumDiff = Integer.MAX_VALUE;
int left = 0;
int right = nums.length - 1;
while (left < right) {
int sum = nums[left] + nums[right];
if (sum == target) {
return 0;
}
int sumDiff = Math.abs(target - sum);
minSumDiff = Math.min(sumDiff, minSumDiff);
if (sum < target) {
left++;
} else {
right--;
}
}
return minSumDiff;
}
}
分析
時間復雜度
對array進行排序,為O(nlogn)。兩個pointer遍歷,為O(n)。所以總共的時間復雜度是O(nlogn)。
空間復雜度
只有個別的單個變量,所以空間復雜度是O(1)。
這道題我的思路和九章一樣。應該是最優解。