编号18: 四数之和

编号18: 四数之和

题意:给定一个包含 n 个整数的数组 nums 和一个目标值 target,判断 nums 中是否存在四个元素 a,b,c 和 d ,使得 a + b + c + d 的值与 target 相等?找出所有满足条件且不重复的四元组。

「注意:」

答案中不可以包含重复的四元组。

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

思路

四数之和,和三数之和是一个思路,都是使用双指针法, 基本解法就是在三数之和 的基础上再套一层for循环。

但是有一些细节需要注意,例如:不要判断nums[k] > target 就返回了,三数之和 可以通过 nums[i] > 0 就返回了,因为 0 已经是确定的数了,四数之和这道题目 target是任意值。(大家亲自写代码就能感受出来)

三数之和的双指针解法是一层for循环num[i]为确定值,然后循环内有left和right下表作为双指针,找到nums[i] + nums[left] + nums[right] == 0。

四数之和的双指针解法是两层for循环nums[k] + nums[i]为确定值,依然是循环内有left和right下表作为双指针,找出nums[k] + nums[i] + nums[left] + nums[right] == target的情况,三数之和的时间复杂度是O(n2),四数之和的时间复杂度是O(n3) 。

那么一样的道理,五数之和、六数之和等等都采用这种解法。

对于三数之和双指针法就是将原本暴力O(n3)的解法,降为O(n2)的解法,四数之和的双指针解法就是将原本暴力O(n4)的解法,降为O(n3)的解法。

之前我们讲过哈希表的经典题目:四数相加II,相对于本题简单很多,因为本题是要求在一个集合中找出四个数相加等于target,同时四元组不能重复。

四数相加II是四个独立的数组,只要找到A[i] + B[j] + C[k] + D[l] = 0就可以,不用考虑有重复的四个元素相加等于0的情况,所以相对于本题还是简单了不少!

大家解决一下这两道题目就能感受出来难度的差异。

代码

//双指针求解四数之和
    public static List<List<Integer>> FourSum(int[] arr, int target) {
        List<List<Integer>> result = new ArrayList<>();
        for (int k = 0; k < arr.length; k++) {
            // 去重
            if (k > 0 && arr[k] == arr[k - 1]) {
                continue;
            }
            for (int i = k + 1; i < arr.length; i++) {
                // 正确去重方法
                if (i > k + 1 && arr[i] == arr[i - 1]) {
                    continue;
                }
                int left = i + 1;
                int right = arr.length - 1;
                while (right > left) {
                    if (arr[k] + arr[i] + arr[left] + arr[right] > target) {
                        right--;
                    } else if (arr[k] + arr[i] + arr[left] + arr[right] < target) {
                        left++;
                    } else {
                        List<Integer> list = new ArrayList<>();
                        list.add(arr[i]);
                        list.add(arr[k]);
                        list.add(arr[left]);
                        list.add(arr[right]);
                        result.add(list);

                        // 去重逻辑应该放在找到一个四元组之后
                        while (right > left && arr[right] == arr[right - 1]) right--;
                        while (right > left && arr[left] == arr[left + 1]) left++;

                        // 找到答案时,双指针同时收缩
                        right--;
                        left++;
                    }
                }
            }

        }
        return result;
    }


posted @ 2021-03-06 20:31  胡木杨  阅读(110)  评论(0)    收藏  举报