No.018:4Sum

问题:

Given an array S of n integers, are there elements a, b, c, and d in S such that a + b + c + d = target?

Find all unique quadruplets in the array which gives the sum of target.

Note: The solution set must not contain duplicate quadruplets.

For example, given array S = [1, 0, -1, 0, -2, 2], and target = 0.

A solution set is:

[

  [-1,  0, 0, 1],

  [-2, -1, 1, 2],

  [-2,  0, 0, 2]

]

官方难度:

Medium

翻译:

给定一个长度为n的无序数组S和目标数字target,在数组S中找出4个整数a,b,c,d,使a+b+c+d=target。

找出所有的可能解,且解集中不包含重复项。

例子:

数组S:{ 1, 0, -1, 0, -2, 2},目标值target=0。

解集为[ [-1,0,0,1],[-2,-1,1,2],[-2,0,0,2] ]。

  1. 显然,本题是No.001(Two Sum),No.015(3Sum),No.016(3Sum Closest)更深一步的讨论。在解决这道问题时,不妨考虑一种解法,适用于5Sum,6Sum之后一系列的问题。我可以将这些问题总结归纳为:kSum问题,k>=2。
  2. 显然是需要使用递归,而递归的终点就是2Sum问题。
  3. 将递归方法独立出来,在进入递归之前,优先给数组排序(如k=2,这种做法会略微影响性能)。
  4. 2Sum问题,仍然使用夹逼的原则,同时维护两侧的previous值。增加优化策略:当前左值大于目标数且当前左值大于0,return;当2倍前左值大于目标值,或2倍当前右值小于目标值,return。
  5. 当k>2时,做类似2Sum的优化策略,将当前目标值-当前值,作为递归入参的目标值,同时传入当前索引值,进行递归。
  6. 获得递归的解集,循环加入当前值到解集中返回。
  7. 注意入参检查。

解题代码:

 1     public static List<List<Integer>> fourSum(int[] nums, int target) {
 2         if (nums == null || nums.length < 4) {
 3             throw new IllegalArgumentException("Input error");
 4         }
 5         Arrays.sort(nums);
 6         return kSum(nums, 0, target, 4);
 7     }
 8 
 9     private static List<List<Integer>> kSum(int[] nums, int startIndex, int target, int kSum) {
10         List<List<Integer>> result = new LinkedList<>();
11         // 递归终点是2Sum问题
12         if (kSum == 2) {
13             int left = startIndex, right = nums.length - 1;
14             int preLeft = Integer.MIN_VALUE, preRight = Integer.MIN_VALUE;
15             while (left < right) {
16                 if (nums[left] > target && nums[left] > 0) {
17                     return result;
18                 }
19                 if (2 * nums[left] > target || 2 * nums[right] < target) {
20                     return result;
21                 }
22                 if (nums[left] == preLeft) {
23                     left++;
24                     continue;
25                 }
26                 if (nums[right] == preRight) {
27                     right--;
28                     continue;
29                 }
30                 int sum = nums[left] + nums[right];
31                 if (sum == target) {
32                     List<Integer> list = new LinkedList<>();
33                     list.add(nums[left]);
34                     list.add(nums[right]);
35                     result.add(list);
36                 }
37                 if (sum < target) {
38                     preLeft = nums[left];
39                     left++;
40                 } else {
41                     preRight = nums[right];
42                     right--;
43                 }
44             }
45         } else {
46             // 大于2Sum问题,使用递归
47             int previous = Integer.MAX_VALUE;
48             for (int i = startIndex; i < nums.length - 1; i++) {
49                 if (nums[i] > target && nums[i] > 0) {
50                     return result;
51                 }
52                 // target值的范围超过k个极值
53                 if (kSum * nums[i] > target || kSum * nums[nums.length - 1] < target) {
54                     return result;
55                 }
56                 if (nums[i] == previous) {
57                     continue;
58                 }
59                 int tempTarget = target - nums[i];
60                 // 开启递归
61                 List<List<Integer>> tempResult = kSum(nums, i + 1, tempTarget, kSum - 1);
62                 for (List<Integer> a : tempResult) {
63                     a.add(nums[i]);
64                     result.add(a);
65                 }
66                 previous = nums[i];
67             }
68         }
69         return result;
70     }
fourSum

相关链接:

https://leetcode.com/problems/4sum/

https://github.com/Gerrard-Feng/LeetCode/blob/master/LeetCode/src/com/gerrard/algorithm/medium/Q018.java

PS:如有不正确或提高效率的方法,欢迎留言,谢谢!

原文地址:https://www.cnblogs.com/jing-an-feng-shao/p/5973421.html