力扣题目-四数之和

86 阅读5分钟

题目描述

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

  • 0 <= a, b, c, d < n
  • abc 和 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

解答(C++)

题目分析

本题要求在给定的整数数组 nums 里找出所有不重复的四元组,这些四元组中的元素之和要等于目标值 target。需要注意以下几点:

  1. 四元组里的元素索引 abcd 要满足 0 <= a, b, c, d < n,并且它们互不相同。
  2. 若两个四元组的元素一一对应,就认为这两个四元组是重复的,重复的四元组只能返回一次。
  3. 最终结果可以按任意顺序返回。

示例分析

以输入 nums = [1,0,-1,0,-2,2]target = 0 为例:

  1. 首先对数组进行排序,得到 [-2, -1, 0, 0, 1, 2]

  2. 外层循环选择第一个元素 -2,内层循环选择第二个元素 -1

  3. 初始化左指针指向 0,右指针指向 2。计算 -2 + (-1) + 0 + 2 = -1 < 0,说明和太小,将左指针右移。

  4. 左指针指向另一个 0,计算 -2 + (-1) + 0 + 2 = -1 < 0,和仍然太小,继续右移左指针。

  5. 左指针指向 1,计算 -2 + (-1) + 1 + 2 = 0,找到了一个满足条件的四元组 [-2, -1, 1, 2],将其加入结果集。然后移动左右指针并去重。

  6. 继续外层和内层循环,重复上述步骤,直到遍历完所有可能的组合。

通过以上步骤,我们可以找出所有满足条件的不重复四元组。

代码

class Solution {
public:
    vector<vector<int>> fourSum(vector<int>& nums, int target) {
        vector<vector<int>> quadruplets;
        if (nums.size() < 4) {
            return quadruplets;
        }
        sort(nums.begin(), nums.end());
        int length = nums.size();
        for (int i = 0; i < length - 3; i++) {
            if (i > 0 && nums[i] == nums[i - 1]) {
                continue;
            }
            if ((long) nums[i] + nums[i + 1] + nums[i + 2] + nums[i + 3] > target) {
                break;
            }
            if ((long) 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] == nums[j - 1]) {
                    continue;
                }
                if ((long) nums[i] + nums[j] + nums[j + 1] + nums[j + 2] > target) {
                    break;
                }
                if ((long) nums[i] + nums[j] + nums[length - 2] + nums[length - 1] < target) {
                    continue;
                }
                int left = j + 1, right = length - 1;
                while (left < right) {
                    long sum = (long) nums[i] + nums[j] + nums[left] + nums[right];
                    if (sum == target) {
                        quadruplets.push_back({nums[i], nums[j], nums[left], nums[right]});
                        while (left < right && nums[left] == nums[left + 1]) {
                            left++;
                        }
                        left++;
                        while (left < right && nums[right] == nums[right - 1]) {
                            right--;
                        }
                        right--;
                    } else if (sum < target) {
                        left++;
                    } else {
                        right--;
                    }
                }
            }
        }
        return quadruplets;
    }
};

代码分析

class Solution {
public:
    vector<vector<int>> fourSum(vector<int>& nums, int target) {
        // 用于存储最终找到的四元组
        vector<vector<int>> quadruplets;
        // 如果数组元素少于 4 个,无法构成四元组,直接返回空结果
        if (nums.size() < 4) {
            return quadruplets;
        }
        // 对数组进行排序,方便后续去重和使用双指针法
        sort(nums.begin(), nums.end());
        // 获取数组的长度
        int length = nums.size();
        // 第一个数的循环,范围是从 0 到 length - 3
        for (int i = 0; i < length - 3; i++) {
            // 去重操作,如果当前数和前一个数相同,跳过当前循环
            if (i > 0 && nums[i] == nums[i - 1]) {
                continue;
            }
            // 如果当前数和接下来的三个最小数之和大于目标值,由于数组已排序,后续组合的和只会更大,直接跳出循环
            if ((long) nums[i] + nums[i + 1] + nums[i + 2] + nums[i + 3] > target) {
                break;
            }
            // 如果当前数和最后三个最大数之和小于目标值,说明当前数太小,继续下一个数
            if ((long) nums[i] + nums[length - 3] + nums[length - 2] + nums[length - 1] < target) {
                continue;
            }
            // 第二个数的循环,范围是从 i + 1 到 length - 2
            for (int j = i + 1; j < length - 2; j++) {
                // 去重操作,如果当前数和前一个数相同,跳过当前循环
                if (j > i + 1 && nums[j] == nums[j - 1]) {
                    continue;
                }
                // 如果当前两个数和接下来的两个最小数之和大于目标值,由于数组已排序,后续组合的和只会更大,直接跳出循环
                if ((long) nums[i] + nums[j] + nums[j + 1] + nums[j + 2] > target) {
                    break;
                }
                // 如果当前两个数和最后两个最大数之和小于目标值,说明当前两个数组合太小,继续下一个数
                if ((long) nums[i] + nums[j] + nums[length - 2] + nums[length - 1] < target) {
                    continue;
                }
                // 双指针,left 指向 j + 1,right 指向数组末尾
                int left = j + 1, right = length - 1;
                // 双指针循环,直到 left 大于等于 right
                while (left < right) {
                    // 计算当前四个数的和
                    long sum = (long) nums[i] + nums[j] + nums[left] + nums[right];
                    // 如果和等于目标值,将四元组加入结果集
                    if (sum == target) {
                        quadruplets.push_back({nums[i], nums[j], nums[left], nums[right]});
                        // 去重操作,跳过相同的元素
                        while (left < right && nums[left] == nums[left + 1]) {
                            left++;
                        }
                        left++;
                        // 去重操作,跳过相同的元素
                        while (left < right && nums[right] == nums[right - 1]) {
                            right--;
                        }
                        right--;
                    } else if (sum < target) {
                        // 如果和小于目标值,左指针右移,增大和
                        left++;
                    } else {
                        // 如果和大于目标值,右指针左移,减小和
                        right--;
                    }
                }
            }
        }
        // 返回最终结果
        return quadruplets;
    }
};

代码逻辑总结

  1. 边界检查:要是数组元素少于 4 个,就直接返回空结果。
  2. 排序:对数组进行排序,方便后续去重和使用双指针法。
  3. 双重循环:借助两层循环来确定前两个数,并且进行去重操作。
  4. 双指针:针对每一组前两个数,利用双指针 left 和 right 来确定后两个数,从而找到所有满足条件的四元组。
  5. 去重:在每一层循环和双指针移动时,都进行去重操作,避免结果中出现重复的四元组。