LeetCode第15题:三数之和

115 阅读1分钟

题目地址

明确题意:

1,给定一个整数数组nums,在其中找3个整数,它们的下标不相同。

2,3个整数的和等于0。

3,返回满足1,2的不重复的所有三元组。

思路分析:

方式一:暴力解法

三层for循环确定每一个数,筛选出和为0的。然后需要将得到的结果去重(使用哈希表)。

public List<List<Integer>> threeSum(int[] nums) {
        Arrays.sort(nums); // 先将数组排序
        List<List<Integer>> result = new ArrayList<>();
        int n = nums.length;
        Map<Integer, Integer> map = new HashMap<>();
        // 将数组中的所有元素存入哈希表中
        for (int i = 0; i < n; i++) {
            map.put(nums[i], i);
        }
        // 枚举所有可能的三个数的组合
        for (int i = 0; i < n - 2; i++) {
            if (i > 0 && nums[i] == nums[i - 1]) continue; // 跳过重复的元素
            for (int j = i + 1; j < n - 1; j++) {
                if (j > i + 1 && nums[j] == nums[j - 1]) continue; // 跳过重复的元素
                int target = 0 - nums[i] - nums[j];
                if (map.containsKey(target) && map.get(target) > j) {
                    List<Integer> list = new ArrayList<>();
                    list.add(nums[i]);
                    list.add(nums[j]);
                    list.add(target);
                    result.add(list);
                }
            }
        }
        return result;
    }

方式二:双指针+排序(去重)

「不重复」的本质是什么?我们保持三重循环的大框架不变,只需要保证:

  • 第二重循环枚举到的元素不小于当前第一重循环枚举到的元素;
  • 第三重循环枚举到的元素不小于当前第二重循环枚举到的元素。

也就是说,我们枚举的三元组 (a, b, c)(a,b,c) 满足a≤b≤c,保证了只有 (a, b, c)(a,b,c) 这个顺序会被枚举到,而 (b, a, c)(b,a,c)、(c, b, a)(c,b,a) 等等这些不会,这样就减少了重复。要实现这一点,我们可以将数组中的元素从小到大进行排序,随后使用普通的三重循环就可以满足上面的要求。

同时,对于每一重循环而言,相邻两次枚举的元素不能相同,否则也会造成重复。举个例子,如果排完序的数组为[0, 1, 2, 2, 2, 3] 我们使用三重循环枚举到的第一个三元组为 (0, 1, 2)(0,1,2),如果第三重循环继续枚举下一个元素,那么仍然是三元组 (0, 1, 2)(0,1,2),产生了重复。因此我们需要将第三重循环「跳到」下一个不相同的元素,即数组中的最后一个元素 33,枚举三元组 (0, 1, 3)(0,1,3)。

 public List<List<Integer>> threeSum(int[] nums) {
        Arrays.sort(nums); // 先将数组排序
        List<List<Integer>> result = new ArrayList<>();
        int n = nums.length;
        // 枚举所有可能的三个数的组合
        for (int i = 0; i < n - 2; i++) {
            if (i > 0 && nums[i] == nums[i - 1]) continue; // 跳过重复的元素
            int left = i + 1, right = n - 1;
            while (left < right) {
                int sum = nums[i] + nums[left] + nums[right];
                if (sum == 0) {
                    result.add(Arrays.asList(nums[i], nums[left], nums[right]));
                    left++;
                    right--;
                    while (left < right && nums[left] == nums[left - 1]) left++; // 跳过重复的元素
                    while (left < right && nums[right] == nums[right + 1]) right--; // 跳过重复的元素
                } else if (sum < 0) {
                    left++;
                } else {
                    right--;
                }
            }
        }
        return result;
    }