【LeetCode-15,双指针】判断数组中是否存在三数和为零

题目

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

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

示例:

给定数组 nums = [-1, 0, 1, 2, -1, -4],

满足要求的三元组集合为:
[
  [-1, 0, 1],
  [-1, -1, 2]
]

分析

这种题一看就知道需要数组遍历了,需要思考的就是如何更高效地遍历,如何不重复自己已经走过的路,这里采用排序+双指针的方式来实现对这道题的解;接下来就是如何去重了,答案要求不可以存在重复的三元组。首先讲一下思路:

  • 首先对数组进行排序,将数组变为升序数组;

    【LeetCode-15,双指针】判断数组中是否存在三数和为零_第1张图片

  • 开始遍历,i指针指向nums[i],新建一个左指针指向nums[i+1],另外一个右指针指向数组最后一个元素,然后计算三个值的和sum是否为0;

  • 当sum < 0 时,此时i指针还没进入下一循环,是固定的,所以只能将左指针往右移,以期望三值之和能趋进于0或等于0;

  • 当sum > 0时,需要将右指针左移,以期望三值之和能趋近于0或等于0;

    【LeetCode-15,双指针】判断数组中是否存在三数和为零_第2张图片

  • 如果sum == 0,那么此时就可以将三个值新建各数组存起来了,接下来不能只移动一个指针了,需要右指针左移,左指针右移;但是在移动前还需要继续判断,因为题目要求“答案中不可以包含重复的三元组”。

    【LeetCode-15,双指针】判断数组中是否存在三数和为零_第3张图片

  • 如果左指针的下一位与现在相等,即nums[left] == nums[left+1],那么应该跳过这一节点,因为这样会使得结果重复,所以一直left++再判断,直到新的left点的元素不相等为止。

  • 如果右指针的前一位与现在相等,即nums[right] == nums[right-1],那么应该跳过这一节点,因为这样会使得结果重复,所以一直right--再判断,直到新的right点的元素不相等为止。

  • 继续循环判断,直到不满足left < right为止,进入下一循环;

  • 进入新的循环,需要判断nums[i] == nums[i-1],因为新的i指针如果与旧的相同,那么同样会产生重复的结果,需要跳过;

  • 该算法的时间复杂度为O(n^2),n 为数组长度。

接下来说几个无聊优化(鸡肋优化):

  • 如果数组长度小于3,那么压根没有3数之和的可能,返回空数组结果即可;

  • 在每次进入循环的时候,可以判断一下i指针指向的节点的值是否大于0,如果大于0,那么3数之和一定大于0,直接跳过;

  • 数组的遍历并不需要将数组最后两位也遍历,所以不需要 i < nums.length,改为i < nums.length - 2

这优化真够鸡肋,没啥用。

代码实现

public class LeetCode15 {

    public static List<List<Integer>> threeSum(int[] nums) {
        List<List<Integer>> result = new LinkedList<List<Integer>>();
        if (nums.length < 3) return result;

        Arrays.sort(nums);

        for (int i = 0; i < nums.length - 2; i++) {
            if(nums[i] > 0) break; // 如果当前i指针指向数大于0,那么3数之和一定大于0,直接跳出
            int left = i + 1, right = nums.length - 1;
            if (i > 0 && nums[i] == nums[i-1]) continue;
            
            while (left < right) {
                int sum = nums[i] + nums[left] + nums[right];
                if (sum == 0) {
                    result.add(Arrays.asList(nums[i], nums[left], nums[right]));
                    while (left < right && nums[left] == nums[left + 1]) left++;
                    while (left < right && nums[right] == nums[right - 1]) right--;
                    left++;
                    right--;
                } else if (sum < 0)
                    left++;
                else
                    right--;
            }
        }

        return result;
    }

    public static void main(String[] args) {
        int[] nums = new int[]{-1, 0, 1, 2, -1, -4};
        System.out.println(threeSum(nums));
    }

}

你可能感兴趣的:(数据结构与算法)