leetcode刷题日记-【18. 四数之和】

89 阅读1分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第26天,点击查看活动详情

题目描述

给你一个由 n 个整数组成的数组 nums ,和一个目标值 target 。请你找出并返回满足下述全部条件且不重复的四元组 [nums[a], nums[b], nums[c], nums[d]] (若两个四元组元素一一对应,则认为两个四元组重复):

0 <= a, b, c, d < n a、b、c 和 d 互不相同 nums[a] + nums[b] + nums[c] + nums[d] == target 你可以按 任意顺序 返回答案 。

 

示例 1:

输入:nums = [1,0,-1,0,-2,2], target = 0 输出:[[-2,-1,1,2],[-2,0,0,2],[-1,0,0,1]] 示例 2:

输入:nums = [2,2,2,2,2], target = 8 输出:[[2,2,2,2]]  

提示:

  • 1 <= nums.length <= 200
  • -109 <= nums[i] <= 109
  • -109 <= target <= 109

题目元素

给定数组nums,整数target,从数组中找到所有四个位置不同的元素,使他们之和等于target。当两个解答数组中四个元素都相同则视为这两个数组重复,只取一个即可。

解题思路

这道题解题思路和之前的三元素之和相似,都是从数组中取n个元素的和使它等于target,不同的是这里需要取的是四个元素,所以多了一层循环。

这里还多了一个去重的条件条件,需要对获得的四个元素的数组进行去重,要达到去重的目的,首先要确认每一层循环的元素都要比前一层的元素大,并且同一层同一个元素不能多次进行循环。

所以需要先将数组进行排序,排序的目的有两层,一个是方便在循环时进行剪枝操作,另一个是可以在循环时去重。

然后用循环+双指针+剪枝的方式进行求解。

前面两层循环就是遍历第一个元素和第二个元素,双指针就用于确认第三个元素和第四个元素。

首先确定第一个元素位置i和第二个元素位置j,则双指针的左指针位置为j+1,右指针的位置为nums.length-1;

对这四个元素进行累加得到sum,与target的值进行比较;

  • 如果sum=target,则将这四个元素放入结果数组,将左指针向右移动(此时与原元素比增大),右指针向左移动(此时与原元素比减小),循环;
  • 如果sum>target,此时需要将sum减小,所以要移动右指针(数组是有序的),循环;
  • 如果sum<target,此时需要将sum增大,所以要移动左指针(数组是有序的),循环。

循环结束之后则得到解,这里注意整数溢出,需要将累加后的数据转成long。

代码实现

public static List<List<Integer>> fourSum(int[] nums, int target) {
    // 进行排序
    Arrays.sort(nums);
    List<List<Integer>> res = new ArrayList<>();
    int length = nums.length;
    for (int i = 0 ; i < length-3; i ++) {
        // 第一个元素
        // 剪枝操作
        if (i > 0 && nums[i-1] == nums[i]) {
            continue;
        }
        if (0L+nums[i] + nums[i+1] + nums[i+2] + nums[i+3] > target || 0L+nums[i] + nums[length-3]+ nums[length-2]+ nums[length-1] < target) {
            continue;
        }
        for (int j = i+1; j < length-2;j++) {
            if (j > i+1 && nums[j-1] == nums[j]) {
                continue;
            }
            // 剪枝操作
            if (0L+nums[i] + nums[j] + nums[j+1] + nums[j+2] > target || 0L+nums[i] + nums[j]+ nums[length-2]+ nums[length-1] < target) {
                continue;
            }
            // 确定第二个元素
            // 建立双指针
            int left = j + 1;
            int right = length -1;
            while (left < right) {
                long sum = 0L+nums[i] + nums[j] + nums[left] + nums[right];
                if (sum == target) {
                    res.add(Arrays.asList(nums[i],nums[j],nums[left],nums[right]));
                    // 查找到不同的元素
                    while (left  < right && nums[right] == nums[right-1]) {
                        right--;
                    }
                    right--;
                    while (left  < right && nums[left] == nums[left+1]) {
                        left++;
                    }
                    left++;
                } else if (sum > target) {
                    // 查找到不同的元素
                    while (left  < right && nums[right] == nums[right-1]) {
                        right--;
                    }
                    right--;
                } else {
                    // 查找到不同的元素
                    while (left  < right && nums[left] == nums[left+1]) {
                        left++;
                    }
                    left++;
                }
            }
        }
    }
    return res;
}