leetcode-15-三數之和(3sum)-java
題目及測試
package pid015; /*三數之和 給定一個包含 n 個整數的陣列 nums,判斷 nums 中是否存在三個元素 a,b,c ,使得 a + b + c = 0 ?找出所有滿足條件且不重複的三元組。 注意:答案中不可以包含重複的三元組。 例如, 給定陣列 nums = [-1, 0, 1, 2, -1, -4], 滿足要求的三元組集合為: [ [-1, 0, 1], [-1, -1, 2] ] */ import java.util.List; public class main { public static void main(String[] args) { int [][] testTable = {{-1, 0, 1, 2, -1, -4},{0,1,2,-3,-1}}; for (int[] ito : testTable) { test(ito); } } private static void test(int[] ito) { Solution solution = new Solution(); List<List<Integer>> rtn; long begin = System.currentTimeMillis(); for(int i=0;i<ito.length;i++){ System.out.print(ito[i]+" "); } System.out.println(); //開始時列印陣列 rtn= solution.threeSum(ito);//執行程式 long end = System.currentTimeMillis(); System.out.println("rtn=" ); for(int i=0;i<rtn.size();i++){ for(int j=0;j<rtn.get(i).size();j++){ System.out.print(rtn.get(i).get(j)+" "); } System.out.println(); } System.out.println(); System.out.println("耗時:" + (end - begin) + "ms"); System.out.println("-------------------"); } }
解法1
沒想出來
解法2(別人的)
先對陣列進行排序,時間複雜度O(log(n)),然後定好一個數的位置,查詢另外兩個數的和等於-nums[i]的組合,由於陣列排好序了,所以可以從兩邊往中間走,當結果大於0的時候後邊往後退一步,否則前邊進一步,時間複雜度O(n2),所以時間複雜度為O(n2)
// 避免重複!!!! // 避免重複!!!! // 避免重複!!!! public List<List<Integer>> threeSum(int[] nums) { List<List<Integer>> ret = new ArrayList<>(); if (nums == null || nums.length < 3) return ret; int len = nums.length; Arrays.sort(nums); // 注意,對於 num[i],尋找另外兩個數時,只要從 i+1 開始找就可以了。 // 這種寫法,可以避免結果集中有重複,因為陣列時排好序的, //所以當一個數被放到結果集中的時候,其後面和它相等的直接被跳過。 for (int i = 0; i < len; i++) { // 可省,目的減少無意義的迴圈 if (nums[i] > 0) break; // 避免重複!!!! if (i > 0 && nums[i] == nums[i - 1]) continue; // 往後找,避免重複 int begin = i + 1; int end = len - 1; while (begin < end) { int sum = nums[i] + nums[begin] + nums[end]; if (sum == 0) { List<Integer> list = new ArrayList<>(); list.add(nums[i]); list.add(nums[begin]); list.add(nums[end]); ret.add(list); begin++; end--; // 避免重複!!!! while (begin < end && nums[begin] == nums[begin - 1]) begin++; while (begin < end && nums[end] == nums[end + 1]) end--; } else if (sum > 0) end--; else begin++; } } return ret; }
解法3(別人的)
首先是求解:因為要求3個數,如果我們固定其中1個數,再用求“和為某值的2個數的組合”的解法,就能把剩下的2個數求出來。因
此,先對陣列進行非遞減排序,這樣整個陣列的數就由小到大排列。i 的取值由 0 至 n-1,對每一個i,我們求當num[i]是解當中的其
中一個數時,其他的2個數。設有指標p指向陣列頭(實際只要p從i+1開始),q指向陣列尾,sum = num[i] + num[p]+ num[q],因為num[i]
是一定在解中的,所以如果sum < 0,因為num[q]已經不能增大,所以說明num[p]太小了,這時p需要向後移動,找一個更大的數。
同理,sum > 0,說明num[q]太大了,需要q向前移動。當sum == 0時,說明找到了一個解。但找到了一個解,並不說明解中有num[i]的
所有解都找到了,因此p或q還需要繼續移動尋找其他的解,直到p == q為止。
上面是求解的過程,那麼去重怎麼做?去重就在於和之前的解進行比較,但我們不需要比較所有的解,這裡有一個技巧。
-
如果num[i] = num[i - 1],說明剛才i-1時求的解在這次肯定也會求出一樣的,所以直接跳過不求;
-
其實指標p不需要從陣列頭開始,因為如果num[i]所在的解中如果有i之前的數,設其位置為j,那麼我們求num[j]時,肯定把num[i]
也找出來放到和num[j]一起的解裡了,所以指標p其實應該從i+1開始,即初始時p = i + 1, q = num.size() - 1;
-
當sum == 0,我們儲存了當前解以後,需要num[i]在解中的其他的2個數組合,這個時候,肯定是p往後或者q往前,如果++p,發
現其實num[p] == num[p-1],說明這個解肯定和剛才重複了,再繼續++p。同理,如果–q後發現num[q] == num[q+1],繼續–q。
這個去重操作主要針對這種有多個同值的陣列,如:-3, 1,1,1, 2,2,3,4。
public class Solution {
public List<List<Integer>> threeSum(int[] nums) {
List<List<Integer>> list = new ArrayList<List<Integer>>();
int n = nums.length;
Arrays.sort(nums);
for(int i=0;i<n;i++){
if(i!=0 && nums[i]==nums[i-1]) continue;
int sum = 0;
int p = i+1,q = n-1;
while(p<q){
sum = nums[i]+nums[p]+nums[q];
if(sum==0){
List<Integer> item = new ArrayList<Integer>();
item.add(nums[i]);
item.add(nums[p]);
item.add(nums[q]);
list.add(item);
while(++p<q && nums[p-1]==nums[p]){
}
while(--q>p && nums[q+1]==nums[q]){
}
}
else if(sum>0){
q--;
}
else{
p++;
}
}
}
return list;
}
}