給你一個整數數組?
nums
?,判斷是否存在三元組?[nums[i], nums[j], nums[k]]
?滿足?i != j
、i != k
且?j != k
?,同時還滿足?nums[i] + nums[j] + nums[k] == 0
?。請你返回所有和為?0
?且不重復的三元組。注意:答案中不可以包含重復的三元組。
?
示例 1:
輸入:nums = [-1,0,1,2,-1,-4] 輸出:[[-1,-1,2],[-1,0,1]] 解釋: nums[0] + nums[1] + nums[2] = (-1) + 0 + 1 = 0 。 nums[1] + nums[2] + nums[4] = 0 + 1 + (-1) = 0 。 nums[0] + nums[3] + nums[4] = (-1) + 2 + (-1) = 0 。 不同的三元組是 [-1,0,1] 和 [-1,-1,2] 。 注意,輸出的順序和三元組的順序并不重要。示例 2:
輸入:nums = [0,1,1] 輸出:[] 解釋:唯一可能的三元組和不為 0 。示例 3:
輸入:nums = [0,0,0] 輸出:[[0,0,0]] 解釋:唯一可能的三元組和為 0 。 https://leetcode.cn/problems/3sum/
這題的題解我看了一個多小時,啊,很專注的盯了一個小時才看懂,下面直接放上題解,做個記錄。
我說CSDN能不能給手機加個代碼編輯器啊!!!
https://leetcode.cn/problems/3sum/solution/hua-jie-suan-fa-15-san-shu-zhi-he-by-guanpengchn/
大家在評論區對這個算法的反饋都挺好,但我一開始沒看懂,所以不覺得好,以下是大家和我對這個算法的一些疑惑點
- 為啥?if(nums == null || len < 3) return ans;
不知道有沒有對這里有疑惑,我反正沒有,但是還是說一下。
這里相當于一個參數檢驗,如果沒有提供nums,或者長度小于3(不滿足題目要求的三數之和,起碼得給3個數吧,不然還做啥),就可以直接終止函數了。
- 為啥 if(nums[i] > 0) break; // 如果當前數字大于0,則三數之和一定大于0,所以結束循環為啥?if(i > 0 && nums[i] == nums[i-1]) continue; // 去重
這里我一開始也不懂的,憑什么i大于0了,就直接結束了?
這里要注意,題解做了個從小到大的排序。
如果這時候i已經大于0了,那么L,R對應的值也必然大于0(該算法的思路就是L,R在i之后),那么i大于0的時候,后面怎么組合,三數之和都不會等于0了。
- 為啥 push完了,i不++,而是繼續下一輪L++, R--
這里我一開始也困惑,為啥i不++,甚至還能繼續用來push(題目不是說什么ijk不相等,不重復之類的話嗎)。
最后我懷疑是不是自己審錯題了,于是我又重新看了一遍題目,以及示例1,茅塞頓開好吧。
i,j,k是不能相等,但是最終結果的[[...],[...],[...]],其中的每一個[...],題目并沒有說,i 只允許被其中一個使用一次,也就是每一個[...]都可以用這個i用一次。
- 為啥?while (L<R && nums[L] == nums[L+1]) L++; // 去重 while (L<R && nums[R] == nums[R-1]) R--; // 去重
這個地方已經有一些題友開始不懂了,我當然一開始也不理解了,怎么老是去重啊,這個算法的小細節怎么搞這么多,普通人真的能想到嗎?
那為什么要做這一步呢?還是得審題!!!
題目最后一個注意說: 答案中不可以包含重復的三元組。
那題解的這個去重操作又是放在push之后做的,我們來看一下。
如果剛push完i L R,準備進行下一輪酣暢淋漓的sum === 0判斷,如果恰好L與上一個L的值相等,會怎么樣?nums[i]不變,是上一個i,L相等的話就是上一個L,滿足sum等于0的話,nums[R]的值也只能和上一個R相等,那這里不就有重復了嗎,無法滿足題目給的貼心注意提示!即使一直沒找到滿足的R,這里的相等的L也是沒必要一直參與sum的計算的,因此這里直接while處理到不相等為止就好了,R也是同理。
- 為啥?else if (sum < 0) L++; else if (sum > 0) R--;
這里應該不會有疑惑,但還是說一下。
因為做了排序,如果sum小于0了,那就是整體偏負唄,把L往大整點,大于0就把R整小點,跟那啥秤砣一樣,挪動挪動就好。