给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有和为 0 且不重复的三元组。

    注意:答案中不可以包含重复的三元组。

    1. 输入:nums = [-1,0,1,2,-1,-4]
    2. 输出:[[-1,-1,2],[-1,0,1]]

    三重for循环,超时典范:

    1. class Solution {
    2. public List<List<Integer>> threeSum(int[] nums) {
    3. Set s = new HashSet();
    4. for(int i = 0;i<nums.length; i++){
    5. for(int j = i+1; j<nums.length; j++){
    6. for(int k = j+1; k<nums.length; k++){
    7. if(nums[i]+nums[j]+nums[k]==0){
    8. ArrayList<Integer> list = new ArrayList();
    9. list.add(nums[i]);
    10. list.add(nums[j]);
    11. list.add(nums[k]);
    12. Collections.sort(list);
    13. s.add(list);
    14. }
    15. }
    16. }
    17. }
    18. return new ArrayList<>(s);
    19. }
    20. }

    【题解】:
    题解都是直接丢双指针的解题思路,没有讲如何从暴力解逐渐推到双指针解法的(大佬的常规思路都是我难以企及的灵光一现),下面讲一下我的理解: 首先,如果用暴力解,用一个三重循环遍历那么时间复杂度在O(Leetcode15. 三数之和(×) - 图1),然后稍微进行优化,根据题目:找到三元组不能重复

    可以想到,如果要排序(能保证重复出现的数字在一起,并且时间复杂度为O(NlogN),没啥影响)
    可以在第二重循环的枚举中找到不小于当前第一重循环的枚举元素
    和第三重循环同理,找到不小于第二重循环的枚举元素
    => 那么能想到了排序,但是本质上还是三重循环,那么时间复杂度还是O(Leetcode15. 三数之和(×) - 图2),继续优化,将下面的两重循环变成一重循环:

    可以发现我们是固定了第一个数然后去找其他两个数的,那么可以将后面两个数看成一个数,那么问题就变成了在有序数组中从[i+1, len-1]这个范围内找到一个符合要求的数,那么就变成了双指针问题,而这个数的值不再是mid,而是两个边界left和right的和。而指针的移动条件就是:如果当前的sum值太大,那么右指针就移动;如果sum太小,那么左指针就移动;如果值正好,那么就是当前值,并且左指针右移,右指针左移(因为是找到所有满足的解);循环的结束条件就是左右指针相遇 而双指针情况下,第二三重循环就从O(Leetcode15. 三数之和(×) - 图3)变成O(N)

    1. class Solution {
    2. public List<List<Integer>> threeSum(int[] nums) {
    3. //在第一重循环中,判断当前数是否和前一个数一样,如果一样,就跳过本次循环
    4. //而在双指针中,如果找到一组正确解后,需要将left和right分别右移/左移到和当前值不一样的地方
    5. Arrays.sort(nums);
    6. List<List<Integer>> res = new ArrayList<>();
    7. for(int i =0;i<nums.length-2;i++){
    8. int base = nums[i];
    9. if(base>0){//按顺序排序,base都大于0了后面应该没有了
    10. return res;
    11. }
    12. //断当前数是否和前一个数一样,如果一样,就跳过本次循环
    13. if(i>0&&nums[i]==nums[i-1]){//
    14. continue;
    15. }
    16. int left = i + 1;
    17. int right = nums.length - 1;
    18. while(left<right){
    19. if(nums[i]+nums[left]+nums[right]==0){
    20. List<Integer> list = new ArrayList<>();
    21. list.add(nums[i]);
    22. list.add(nums[left]);
    23. list.add(nums[right]);
    24. res.add(list);
    25. //直到找到和下一次结果不一样的
    26. while(left<right&&nums[left]==nums[left+1]){
    27. left++;
    28. }
    29. while(left<right&&nums[right]==nums[right-1]){
    30. right--;
    31. }
    32. left++;
    33. right--;
    34. }else if(nums[i]+nums[left]+nums[right]>0){
    35. right--;
    36. }else{
    37. left++;
    38. }
    39. }
    40. }
    41. return res;
    42. }
    43. }