15. 三数之和

236 阅读1分钟

image.png

大体思路:a + b + c = 0 转化为 a + b = -c

方法二:外层for确定-c,内层用双指针替代两个for. 排序判断去重

考虑例子:

E26B7612C8343EB70C3E02F0BAB1308B.png 因此:

  • 给数组排序,枚举的三元组 (a, b, c) 满足大小顺序,保证了只有(a,b,c) 这个顺序会被枚举到,而 (b,a,c)、(c,b,a)不会

  • 之后对nums[i] nums[j] nums[k]都要去重

//方法二 排序+双指针,用双指针代替了两个for
class Solution {
  public List<List<Integer>> threeSum(int[] nums) {
      Arrays.sort(nums); // 先排序!!!
      List<List<Integer>> res = new ArrayList<>();
      for (int i = 0; i < nums.length; i++) {
          if (nums[i] > 0) {//如果基准数 > 0,直接返回结果,后面的不用看了
              return res;
          }
          if (i > 0 && nums[i] == nums[i - 1]  ) {//如果基准数重复
              continue;
          }
          int j = i + 1;//j为nums[i+1,end]的首
          int k = nums.length - 1;//k为尾
          while (j < k) {
              // 【把常出现的情况放前面 优化速度】
              if (nums[i] + nums[j] + nums[k] < 0) {//sum小于0,期待更大的sum,左指针右移
                   j++;
              } else if (nums[i] + nums[j] + nums[k] > 0) {//sum大于0,期待更小的sum,右指针左移
                   k--;
              } else { // nums[i] + nums[j] + nums[k] == 0
                   List<Integer> tmp = new ArrayList<>();
                   tmp.add(nums[i]);
                   tmp.add(nums[j]);
                   tmp.add(nums[k]);
                   res.add(tmp);
                   //找到一例三元组后,判断左右指针是否重复
                   //如果左指针对应的数重复,注意防止00000的情况越界
                   while (j + 1 < nums.length && nums[j] == nums[j + 1]) {
                       j++;
                   }
                   //如果右指针对应的数重复,注意防止00000的情况越界
                   while (k - 1 >=0 && nums[k] == nums[k - 1]) {
                       k--;
                   }
                   //收缩区间
                   j++;
                   k--;
              }
          }
      }
      return res;
  }
}

核心代码

for (i遍历) {
	//i判断重复
    while (left < right) {
    	//双指针left,right分情况移动并判断重复
    
    }
}

方法一: 外层for循环确定-c,内层两数之和,set去重

//方法一
/**
 * a + b + c = 0 转化为 a + b = -c
 * 利用two sum的HashMap做法
 * 利用set去重
 */
class Solution {
    public List<List<Integer>> threeSum(int[] nums) {
        List<List<Integer>> res = new ArrayList<>();
        Set<List<Integer>> tmpres = new HashSet<>();
        for (int i = 0; i < nums.length; i++) {//外层循环遍历数组,确定新的目标值-c
            Set<Integer> set = new HashSet<>();//Set声明的位置要注意,应该是确定新的目标值之后
            for (int j = i + 1; j < nums.length; j++) {//内层循环,两数之和
                if (set.contains(-nums[i] - nums[j])) {
                    List<Integer> list = new ArrayList<>();
                    list.add(nums[i]);
                    list.add(-nums[i]- nums[j]);
                    list.add(nums[j]);
                    Collections.sort(list);//对某一符合条件的三元组排好序
                    tmpres.add(list);//加到结果set中,对于重复的元素,set不会再次添加
                } else {
                    set.add(nums[j]);
                }
            }
        }
        return new ArrayList<>(tmpres);//set转为list
    }
}