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

    示例:给定数组 nums = [-1, 0, 1, 2, -1, -4], 满足要求的三元组集合为: [ [-1, 0, 1], [-1, -1, 2] ]


    思路分析:
    双指针法用在求和、大小比较类的数组题目里时,大前提往往是:该数组必须有序。否则双指针根本无法帮助我们缩小定位的范围,压根没有意义。因此这道题的第一步是用 sort() 将数组排序。
    然后遍历数组,固定遍历到的数字,左指针指向该数字后面紧邻着的位置,右指针指向数组末尾,左右指针同时向中间移动
    image.png
    TL,DR

    1. 数组有序之后,有规律有利于做题,观察执行示例的输出数组里面就是有序的
    2. 有序数组使用双指针——对撞指针
    3. 边界条件:外层i 增加的时候,左指针右移,右指针左移,排除元素重复

    在左右指针对撞之前,计算两个指针对应的数字与固定数字的和:

    • 如果和等于0,得到目标组合
    • 如果和小于0,说明左指针的数偏小,左指针右移
    • 如果和大与0,说明右指针的数偏大,右指针左移

    tips:本题目要求返回不重复的三元组,所以遍历数组的时候要加以判断

    1. var threeSum = function(nums) {
    2. const arr = nums.sort((a, b) => a-b)
    3. const res = []
    4. const len = arr.length
    5. for(let i=0;i<len-2;i++){
    6. if(i>0 && arr[i] === arr[i-1]){ // 跳过重复元素
    7. continue
    8. }
    9. let left = i+1
    10. let right = len-1
    11. while(left < right){
    12. if(arr[i]+ arr[left]+arr[right] > 0){
    13. right--
    14. // 处理右指针左移时重复的情况
    15. while(left < right && arr[right] === arr[right+1]){
    16. right--
    17. }
    18. }else if(arr[i]+ arr[left]+arr[right] < 0){
    19. left++
    20. // 处理左指针右移时重复的情况
    21. while(left < right && arr[left] === arr[left-1]){
    22. left++
    23. }
    24. }else{
    25. res.push([arr[i], arr[left], arr[right]])
    26. left++
    27. while(left < right && arr[left] === arr[left-1]){
    28. left++
    29. }
    30. right--
    31. while(left < right && arr[right] === arr[right+1]){
    32. right--
    33. }
    34. }
    35. }
    36. }
    37. return res
    38. };

    在上面这道题中,左右指针一起从两边往中间位置相互迫近,这样的特殊双指针形态,被称为“对撞指针”。
    当出现有序和数组——这两个关键字的时候,普通双指针走不通,立刻联想到对撞指针!对撞指针可以帮助我们缩小问题的范围,但是前提条件必须是有序的数组,所以有些时候我们发现思路走不通的时候可以先对数组进行排序,创造解题的条件。


    另一种答案,自己写的,感觉边界条件更清晰

    /**
     * @param {number[]} nums
     * @return {number[][]}
     */
    var threeSum = function(nums) {
      let res = []
      const arr = nums.sort((a,b) => a-b)
      const len = arr.length
      for(let i=0;i< arr.length -2; i++){
        if(i>0 &&  arr[i] === arr[i-1]){
          continue
        }
        // 定义双指针
        let left = i+1
        let right = len - 1
        while( left < right){
          if(arr[i] + arr[left] + arr[right] === 0 ){
            res.push([arr[i], arr[left], arr[right]])
            left++
            // 左指针右移避免重复
            while(arr[left] === arr[left-1]){
              left++
            }
            right--
          }else if(arr[i] + arr[left] + arr[right] < 0){
            left++
          }else{
            right--
          }
        }
      }
      return res
    
    };