LeetCode - 18 四数之和

目录

题目来源

题目描述

示例

提示

题目解析

算法源码


题目来源

18. 四数之和 - 力扣(LeetCode)

题目描述

给你一个由 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
  • -10^9 <= nums[i] <= 10^9
  • -10^9 <= target <= 10^9

题目解析

本题是LeetCode - 15 三数之和_伏城之外的博客-CSDN博客的拓展题。

本题是需要找出所有和为target的四元组,且四元组不能重复。看本题前,建议先将前面链接的三数之和看明白。

四数之和,相当于比三数之和多了一个数。

前面求解三数之和时,我们是固定三元组中的最小值,然后通过双指针来找匹配该最小值的其他两个数。

而四数之和的解法,和三数之和类似,只是四数之和,我们需要固定四元组中最小的两个值,然后通过双指针来找匹配这两个最小值得其他两个数。

大家和对比下面四数之和,和前面三数之和得代码,其实就是多了套了一层for循环。

本题的去重逻辑也和三数之和一样。

具体解析可以看前面链接博客。

关于本题的剪枝优化:

三数之和那题的target=0,而本题四数之和的target为一个任意值,可正,可负,可0。

前面三数之和的剪枝优化,是三元组的最小值nums[i] > 0时,则此时三元组之和必然也是>0,且后续的nums[i+1]对应的三元组之和也肯定大于0,因此当nums[i] > 0时,可以直接break掉三指针运动,达到剪枝优化效果。

但是本题四数之和,我们不能说

四元组的最小值nums[i] > target,那么此时的四元组之和也是>target

比如nums = [-4, -3, -2, -1],target = -10

四元组[-4,-3,-2,-1]中nums[i] > target的,但是此时我们不能直接break掉四指针运动,因为后续的nums[j],nums[l],nums[r]也可能是负数,即nums[i] 加上后续三个数,不一定使得i,j,l,r四元组之和变大,因此nums[i] > target,但是四元组之和也有可能 == target。

本题的剪枝优化其实也可以做,但是又局限性,即只有nums[i] > 0,即i,j,l,r指向数都是正数时,此时num[i] > target可以进行剪枝优化。

同理如果nums[j] > 0 && nums[i] + nums[j] > target,也一样可以剪枝。

JS算法源码

/**
 * @param {number[]} nums
 * @param {number} target
 * @return {number[][]}
 */
var fourSum = function (nums, target) {
  const ans = [];

  nums.sort((a, b) => a - b);

  for (let i = 0; i < nums.length; i++) {
    // 去重
    if (i > 0 && nums[i] == nums[i - 1]) continue;

    for (let j = i + 1; j < nums.length; j++) {
      // 去重
      if (j > i + 1 && nums[j] == nums[j - 1]) continue;

      let l = j + 1;
      let r = nums.length - 1;

      while (l < r) {
        const sum = nums[i] + nums[j] + nums[l] + nums[r];

        if (sum > target) {
          r--;
        } else if (sum < target) {
          l++;
        } else {
          ans.push([nums[i], nums[j], nums[l], nums[r]]);

          // 去重
          while (l + 1 < r && nums[l] == nums[l + 1]) l++;
          // 去重
          while (r - 1 > l && nums[r] == nums[r - 1]) r--;

          l++;
          r--;
        }
      }
    }
  }

  return ans;
};

四数之和 - 提交记录 - 力扣(LeetCode)

LeetCode - 18 四数之和_第1张图片

Java算法源码

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

class Solution {
  public List> fourSum(int[] nums, int target) {
    List> ans = new ArrayList<>();

    Arrays.sort(nums);

    for (int i = 0; i < nums.length; i++) {
      // 剪枝
      if (nums[i] > 0 && nums[i] > target) break;

      // 去重
      if (i > 0 && nums[i] == nums[i - 1]) continue;

      for (int j = i + 1; j < nums.length; j++) {
        // 剪枝
        if (nums[j] > 0 && nums[i] + nums[j] > target) break;

        // 去重
        if (j > i + 1 && nums[j] == nums[j - 1]) continue;

        int l = j + 1;
        int r = nums.length - 1;

        while (l < r) {
          long sum = (long) nums[i] + nums[j] + nums[l] + nums[r];

          if (sum > target) {
            r--;
          } else if (sum < target) {
            l++;
          } else {
            ArrayList tmp = new ArrayList<>();
            Collections.addAll(tmp, nums[i], nums[j], nums[l], nums[r]);
            ans.add(tmp);

            // 去重
            while (l + 1 < r && nums[l] == nums[l + 1]) l++;
            // 去重
            while (r - 1 > l && nums[r] == nums[r - 1]) r--;

            l++;
            r--;
          }
        }
      }
    }

    return ans;
  }
}

四数之和 - 提交记录 - 力扣(LeetCode)

LeetCode - 18 四数之和_第2张图片

Python算法源码

class Solution(object):
    def fourSum(self, nums, target):
        """
        :type nums: List[int]
        :type target: int
        :rtype: List[List[int]]
        """
        ans = []

        nums.sort()

        for i in range(len(nums)):
            # 剪枝
            if nums[i] > 0 and nums[i] > target:
                break

            # 去重
            if i > 0 and nums[i] == nums[i - 1]:
                continue

            for j in range(i + 1, len(nums)):
                # 剪枝
                if nums[j] > 0 and nums[i] + nums[j] > target:
                    break

                # 去重
                if j > i + 1 and nums[j] == nums[j - 1]:
                    continue

                l = j + 1
                r = len(nums) - 1

                while l < r:
                    total = nums[i] + nums[j] + nums[l] + nums[r]

                    if total > target:
                        r -= 1
                    elif total < target:
                        l += 1
                    else:
                        ans.append([nums[i], nums[j], nums[l], nums[r]])

                        # 去重
                        while l + 1 < r and nums[l] == nums[l + 1]:
                            l += 1

                        # 去重
                        while r - 1 > l and nums[r] == nums[r - 1]:
                            r -= 1

                        l += 1
                        r -= 1

        return ans

四数之和 - 提交记录 - 力扣(LeetCode)

LeetCode - 18 四数之和_第3张图片

你可能感兴趣的:(算法刷题,leetcode,算法,Java,JavaScript,Python)