Loading

Medium | LeetCode 15. 三数之和 | 双指针法

15. 三数之和

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

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

示例 1:

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

示例 2:

输入:nums = []
输出:[]

示例 3:

输入:nums = [0]
输出:[]

提示:

  • 0 <= nums.length <= 3000
  • -105 <= nums[i] <= 105

方法一:

本题和 Easy | LeetCode 1. 两数之和 | 排序+双指针 | HaspMap 类似, 也可使用排序加双指针的方式。先排序, 然后从到右扫描数组, 找三数之和实际上就是找当前位置后面有没有两数之和为 target - num 的数组。

由于题目不允许重复值。所以有两处需要跳过重复值。

  1. 在计算三数之和时, 对于当前值, 要跳过后面的重复值
  2. 在计算两数之和时, 由于采用的是双指针法, 既要跳过左指针的重复值, 也要跳过右指针的重复值。

详细代码如下:

public List<List<Integer>> threeSum(int[] nums) {
	// 先排序
    Arrays.sort(nums);
    List<List<Integer>> res = new ArrayList<>();
    int target = 0;
    int i = 0;
    while (i < nums.length-1) {
        int firstValue = nums[i];
        // 将三数之和问题 转化为 两数之和 问题
        List<List<Integer>> twoSum = twoSum(nums, i + 1, target - firstValue);
        if (twoSum.size() > 0) {
            twoSum.forEach(twoSumList -> {
                List<Integer> threeSum = new ArrayList<>();
                threeSum.add(firstValue);
                threeSum.addAll(twoSumList);
                res.add(threeSum);
            });
        }
        // 同样扫描数组时, 要跳过重复值
        while (i < nums.length - 1 && nums[i] == firstValue) {
            i++;
        }
    }
    return res;
}

public List<List<Integer>> twoSum(int[] nums, int start, int target) {
    int left = start, right = nums.length -1;
    List<List<Integer>> res = new ArrayList<>();
    // 两数之和问题, 双指针法
    while (left < right) {
        int sum = nums[left] + nums[right];
        int leftValue = nums[left], rightValue = nums[right];
        if (sum < target) {
            // 当双指针之和小于目标值时, 左指针向前走
            left++;
        } else if (sum > target) {
            // 当双指针之和大于目标值时, 右指针向左走
            right--;
        } else {
            // 恰好等于目标值时, 先把当前答案添加进去
            List<Integer> twoSum = new ArrayList<>();
            twoSum.add(leftValue);
            twoSum.add(rightValue);
            res.add(twoSum);
            // 然后跳过左边的重复值, 因为题目不允许重复
            while (left < right && nums[left] == leftValue) {
                left++;
            }
            // 跳过右边的重复值
            while (left < right && nums[right] == rightValue) {
                right--;
            }
        }
    }
    return res;
}
posted @ 2021-01-23 22:54  反身而诚、  阅读(50)  评论(0编辑  收藏  举报