給定一個包含n個整數的數組nums,判斷nums中是否存在三個元素a,b,c,使得a+b+c=0,並找出全部知足條件且不重複的三元組。(注:不包含重複的三元組)。面試
例如,給定數組nums=[-1,0,1,2,-1,-4],則知足要求的三元組集合爲:
[ [-1, 0, 1], [-1, -1, 2] ]算法
首先對數組進行排序,用於定位基準點。假設以排序後首個固定元素nums[i](i=0)爲起始點,再以左指針指向nums[i+1](簡略記爲nums[l]),右指針指向nums[len-1](簡略記爲nums[r]),即指向nums[i]右側元素的兩端,計算三數之和是否知足條件爲0。如不知足且和小於0,則L++;反之則R--,有序移動兩端指針,當L與R碰撞時(L>=R)時本輪遍歷結束,右移起始點(i++);如三數之和爲0,知足條件則記錄並繼續移動兩端指針。
圖1:引用自知乎的圖解數組
因爲按小到大排序,所以若是做爲起始點的nums[i]>0,則三數之和必然沒法等於0,此時可結束循環。若是nums[i]==nums[i−1],則說明該元素重複,根據題意須要跳過,避免輸出重複的結果。ide
此外,當三數之和爲0時,假設nums[L]==nums[L+1]會致使結果重複,須要跳過該元素(L++);若是nums[R]==nums[R−1]一樣會致使結果重複,也須要跳過該元素(R--)。整體來講,時間複雜度爲:O(n2)。測試
public static void sum3(int[] nums) { if(nums != null && nums.length > 2) { // 初始化並對數組排序 Arrays.sort(nums); //定位起始點元素右側的兩端指針 int l = 0, r = 0; for (int i = 0; i < nums.length; i++) { // 若是起始點元素大於0,因爲已排序, // 其與右側兩端元素之和必然大於0 if (nums[i] > 0) break; // 若是起始點右移,當前元素與以前相同 // 則須要跳過避免出現重複結果 if (i > 0 && nums[i] == nums[i - 1]) continue; // 定義起始點元素右側的兩端指針 // 分別指向其右側元素數組的左右兩端 l = i + 1; r = nums.length - 1; while (l < r) if (nums[i] + nums[l] + nums[r] == 0) { // 當三數之和爲0,則標記,並有序移動兩端指針 // 與此同時跳太重複元素 while (l < r && nums[l] == nums[l + 1]) l++; while (l < r && nums[r] == nums[r - 1]) r--; l++; r--; } // 如三數之和小於0,則左指針右移,增大三數之和 else if (nums[i] + nums[l] + nums[r] < 0) l++; //如三數之和大於於0,則右指針左移,減少三數之和 else if (nums[i] + nums[l] + nums[r] > 0) r--; } } }
以上僅爲參考,解題思路萬千並不是惟一,請結合具體案例編寫測試用例驗證正確性。同時也請進一步思考是否有更優的算法。指針
參考資料:
https://leetcode-cn.com/problems/3sum/solution/hua-jie-suan-fa-15-san-shu-zhi-he-by-guanpengchn/code