LeetCode 15 3sum 三數之和
題目連結
https://leetcode-cn.com/problems/3sum/
題意
很簡單,就是給出一個數組,3個數一組,找到所有和為0的組。並且要求不能重複。或者說找其中3個數其和為0,找出所有的組合。
題解
很好的一個題了,博主做了3次,前兩次全部TLE,最後才AC了,但是整體思路差不多,就看能不能想到雙指標的操作。前兩次就是太菜了。
先說一個比較簡單的思路,雙指標的思路也有很大程度的重合,也是博主前兩次TLE的思路:直接爆搜+剪枝。首先陣列是要排序的,那麼我們從最小的數開始暴搜,作為第一個數。然後搜第二個、第三個。如果不剪枝的話絕壁是超時的。比較容易想到的剪枝就是和如果大於0的時候可以直接剪掉了。我們可以當nums[i] > 0、nums[i]+nums[j] > 0分別剪去。然後就是第三個數k的範圍。直接O(N^3)的搜尋肯定不行了,那麼k應該至少從0開始。因為i與j兩者的和必須要<=0才有意義,否則就不存在第三個數了。所以可以預先搜尋0或者第一個大於0的數,作為k的開始。預先搜尋也略坑,我們直接把k當最大值反向搜就可以了。
其次,一個坑就是不能要求重複。如果我們直接搜而不考慮去重的情況會這樣,例如資料:
-1,-1,0,1
那麼會出現兩組[-1,0,1]這就是重複了。因為我們已經排序,所以去重的方法也非常簡單,例如i,當i > 0時,如果nums[i] = nums[i-1],我們可以continue,跳過這次搜尋,直接i++。這樣就完成了去重。不能利用set去重,這樣就把重複的數變成一個數了,對於:
-1,-1,2
這種類似的資料而言是非常致命的,至少少算了一組。
上面就是博主TLE兩次的演算法了,很暴力。實際上這樣的演算法時間複雜度可以說是O(N^3)了,雖然看起來剪枝減去了很多,但是TLE還是妥妥的。而用雙指標的話就能將複雜度減為O(n^2),並且仍然能使用上面的剪枝。仍然是遍歷i,但是j與k設定為兩個指標,j = i+1,k = len-1。這樣,如果三數的和為0,那麼就不多說了,j++以及k--,同時加入解集。當三數的和大於0,說明k應該減小,小於0則是j增大。這樣就能在O(n^2)內實現找到所有解。
當然,對於i、j仍然可以判斷大於0來進行一次剪枝。同時也應該考慮去重的情況。
Java 程式碼
import java.util.*; class Solution { public List<List<Integer>> threeSum(int[] nums) { List<List<Integer>> ans = new ArrayList<>(); int len = nums.length; if(len < 2) return ans; Arrays.sort(nums); for(int i = 0;i < len-2;i++){ if(nums[i] > 0) break; if(i > 0){ if(nums[i] == nums[i-1]) continue; } int j = i+1; int k = len-1; while(j < k){ if(nums[i] + nums[j] + nums[k] == 0){ List<Integer> ad = new ArrayList<>(); ad.add(nums[i]); ad.add(nums[j]); ad.add(nums[k]); ans.add(ad); k--; while(k > j && nums[k] == nums[k+1]) k--; j++; while(j < k && nums[j] == nums[j-1]) j++; }else{ if(nums[i] + nums[j] + nums[k] > 0){ k--; }else{ j++; } } } } return ans; } }