LeetCode18.四数之和

18.四数之和

文章目录

      • 18.四数之和
        • 一、题目
        • 二、解法
          • 方法一:双指针法
            • 算法思路
            • 具体实现
            • 算法分析
        • 三、一些拓展
          • 拓展知识:双指针法和排序


一、题目

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

  • 0 <= a, b, c, d < n
  • abcd 互不相同
  • 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

二、解法

方法一:双指针法
算法思路
  1. 首先,我们对输入的数组 nums 进行排序,这样可以使得相同的数字相邻并方便后续的操作。
  2. 接下来,我们使用两层循环遍历数组,第一层循环从左到右选择第一个数字 nums[i],第二层循环从 i+1 到数组末尾选择第二个数字 nums[j]
  3. 在第二层循环中,我们使用双指针 leftright 来寻找剩下的两个数字,使得四个数字的和等于目标值 target
  4. 当我们固定了 nums[i]nums[j] 后,我们将问题转化为在剩下的数组部分中寻找两个数字,使得它们的和等于 target - nums[i] - nums[j]。为了方便操作,我们将 target - nums[i] - nums[j] 记为 newTarget
  5. 我们使用双指针 leftright 在剩下的数组部分中进行搜索。当 left 小于 right 时,我们计算当前的和 sum,如果 sum 等于 newTarget,则找到了一个满足条件的四元组,将其加入结果集。然后我们继续移动指针,并跳过重复的数字,直到找到下一个不同的数字。
  6. sum 小于 newTarget 时,我们将 left 指针右移一位,以增大和的值。当 sum 大于 newTarget 时,我们将 right 指针左移一位,以减小和的值。
  7. 在每一层循环中,我们需要进行一些剪枝操作,以提高效率和避免重复计算。具体包括外层剪枝操作和内层去重操作。
具体实现

基于上述算法思路,下面是具体的实现代码:

class Solution {
public:
    vector<vector<int>> fourSum(vector<int>& nums, int target) {
        sort(nums.begin(), nums.end()); // 对数组进行排序
        vector<vector<int>> result; // 存储结果的数组
        int n = nums.size();
        for (int i = 0; i < n - 3; i++) {
            // 外层剪枝操作
            if (nums[i] > target && nums[i] > 0) {
                break;
            }
            // 外层去重操作
            if (i > 0 && nums[i] == nums[i - 1]) {
                continue;
            }
            for (int j = i + 1; j < n - 2; j++) {
                // 内层剪枝操作
                if (nums[i] + nums[j] > target && nums[j] > 0) {
                    break;
                }
                // 内层去重操作
                if (j > i + 1 && nums[j] == nums[j - 1]) {
                    continue;
                }
                int left = j + 1;
                int right = n - 1;
                while (left < right) {
                    long sum = (long)nums[i] + (long)nums[j] + (long)nums[left] + (long)nums[right];
                    if (sum == target) {
                        result.push_back({nums[i], nums[j], 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 < target) {
                        left++;
                    } else {
                        right--;
                    }
                }
            }
        }
        return result;
    }
};
算法分析

现在我们对上述算法进行一些分析:

  • 时间复杂度:排序的时间复杂度为 O(nlogn),外层循环的时间复杂度为 O(n),内层循环和双指针的时间复杂度为 O(n2)。因此,总的时间复杂度为 O(n3)。
  • 空间复杂度:除了存储结果的数组外,算法的空间复杂度为 O(1),因为我们没有使用额外的空间。

三、一些拓展

拓展知识:双指针法和排序

在上面的解法中,我们使用了双指针法和排序来解决这个问题。下面我们将介绍一下双指针法和排序的相关知识,并提供一个简单的示例代码。

双指针法是一种常用的算法技巧,它通过使用两个指针在数组中移动,从而解决一些问题。在本题中,我们使用了双指针法来寻找满足条件的四元组。通过固定前两个数字,然后使用双指针在剩下的数组部分中查找剩下的两个数字,使得它们的和等于目标值。通过左右指针的移动,我们可以有效地遍历所有可能的组合,并且通过剪枝操作和去重操作来提高效率。

排序在本题中起到了关键的作用。通过对输入数组进行排序,我们可以使得相同的数字相邻,方便后续的操作。排序后,我们可以通过双指针法在有序数组中查找满足条件的四元组。另外,排序还可以帮助我们进行剪枝操作和去重操作,从而减少计算量。

下面是一个简单的示例代码,展示了如何使用双指针法在有序数组中查找两个数的和等于目标值的情况:

#include 
#include 
using namespace std;

vector<vector<int>> twoSum(vector<int>& nums, int target) {
    vector<vector<int>> result;
    int left = 0;
    int right = nums.size() - 1;
    while (left < right) {
        int sum = nums[left] + nums[right];
        if (sum == target) {
            result.push_back({nums[left], nums[right]});
            left++;
            right--;
        } else if (sum < target) {
            left++;
        } else {
            right--;
        }
    }
    return result;
}

int main() {
    vector<int> nums = {1, 2, 3, 4, 5, 6, 7, 8, 9};
    int target = 10;
    vector<vector<int>> result = twoSum(nums, target);
    for (const auto& res : result) {
        for (int num : res) {
            cout << num << " ";
        }
        cout << endl;
    }
    return 0;
}

在上面的示例代码中,我们定义了一个函数 twoSum,它接收一个有序数组 nums 和目标值 target,并返回满足条件的两个数的组合。通过设置左右指针,在遍历过程中不断调整指针的位置,我们可以找到满足条件的组合,并将其添加到结果中。

你可能感兴趣的:(Leetcode刷题,算法,数据结构,双指针)