18. 4Sum #
Problem #
Given an array nums of n integers and an integer target, are there elements a, b, c, and d in nums 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.
Example:
Given array nums = [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]
]
Problem Summary #
Given an array, find all combinations of 4 numbers in the array whose sum is 0.
Solution Approach #
Use a map to precompute and store the sums of any 3 numbers, which can reduce the time complexity to O(n^3). The tricky part of this problem is that when outputting the final solutions, duplicate solutions must not be output. The same number may appear multiple times in the array, and the same number may also be used multiple times, but the final output solutions cannot be duplicated. For example, [-1, 1, 2, -2], [2, -1, -2, 1], and [-2, 2, -1, 1] are duplicate solutions, even if -1 and -2 may appear 100 times and the array indices of -1 and -2 used each time are different.
This problem is an upgraded version of Problem 15, and the ideas are exactly the same. Here, deduplication and sorting are needed. The map records the number of occurrences of each number, then the key array of the map is sorted. Finally, scan this sorted array and find combinations where another 3 numbers can form 0 together with the current number.
The solutions for Problems 15 and 18 are the same.
Code #
package leetcode
import "sort"
// Solution 1: Two pointers
func fourSum(nums []int, target int) (quadruplets [][]int) {
sort.Ints(nums)
n := len(nums)
for i := 0; i < n-3 && nums[i]+nums[i+1]+nums[i+2]+nums[i+3] <= target; i++ {
if i > 0 && nums[i] == nums[i-1] || nums[i]+nums[n-3]+nums[n-2]+nums[n-1] < target {
continue
}
for j := i + 1; j < n-2 && nums[i]+nums[j]+nums[j+1]+nums[j+2] <= target; j++ {
if j > i+1 && nums[j] == nums[j-1] || nums[i]+nums[j]+nums[n-2]+nums[n-1] < target {
continue
}
for left, right := j+1, n-1; left < right; {
if sum := nums[i] + nums[j] + nums[left] + nums[right]; sum == target {
quadruplets = append(quadruplets, []int{nums[i], nums[j], nums[left], nums[right]})
for left++; left < right && nums[left] == nums[left-1]; left++ {
}
for right--; left < right && nums[right] == nums[right+1]; right-- {
}
} else if sum < target {
left++
} else {
right--
}
}
}
}
return
}
// Solution 2: kSum
func fourSum1(nums []int, target int) [][]int {
res, cur := make([][]int, 0), make([]int, 0)
sort.Ints(nums)
kSum(nums, 0, len(nums)-1, target, 4, cur, &res)
return res
}
func kSum(nums []int, left, right int, target int, k int, cur []int, res *[][]int) {
if right-left+1 < k || k < 2 || target < nums[left]*k || target > nums[right]*k {
return
}
if k == 2 {
// 2 sum
twoSum(nums, left, right, target, cur, res)
} else {
for i := left; i < len(nums); i++ {
if i == left || (i > left && nums[i-1] != nums[i]) {
next := make([]int, len(cur))
copy(next, cur)
next = append(next, nums[i])
kSum(nums, i+1, len(nums)-1, target-nums[i], k-1, next, res)
}
}
}
}
func twoSum(nums []int, left, right int, target int, cur []int, res *[][]int) {
for left < right {
sum := nums[left] + nums[right]
if sum == target {
cur = append(cur, nums[left], nums[right])
temp := make([]int, len(cur))
copy(temp, cur)
*res = append(*res, temp)
// reset cur to previous state
cur = cur[:len(cur)-2]
left++
right--
for left < right && nums[left] == nums[left-1] {
left++
}
for left < right && nums[right] == nums[right+1] {
right--
}
} else if sum < target {
left++
} else {
right--
}
}
}
// Solution 3
func fourSum2(nums []int, target int) [][]int {
res := [][]int{}
counter := map[int]int{}
for _, value := range nums {
counter[value]++
}
uniqNums := []int{}
for key := range counter {
uniqNums = append(uniqNums, key)
}
sort.Ints(uniqNums)
for i := 0; i < len(uniqNums); i++ {
if (uniqNums[i]*4 == target) && counter[uniqNums[i]] >= 4 {
res = append(res, []int{uniqNums[i], uniqNums[i], uniqNums[i], uniqNums[i]})
}
for j := i + 1; j < len(uniqNums); j++ {
if (uniqNums[i]*3+uniqNums[j] == target) && counter[uniqNums[i]] > 2 {
res = append(res, []int{uniqNums[i], uniqNums[i], uniqNums[i], uniqNums[j]})
}
if (uniqNums[j]*3+uniqNums[i] == target) && counter[uniqNums[j]] > 2 {
res = append(res, []int{uniqNums[i], uniqNums[j], uniqNums[j], uniqNums[j]})
}
if (uniqNums[j]*2+uniqNums[i]*2 == target) && counter[uniqNums[j]] > 1 && counter[uniqNums[i]] > 1 {
res = append(res, []int{uniqNums[i], uniqNums[i], uniqNums[j], uniqNums[j]})
}
for k := j + 1; k < len(uniqNums); k++ {
if (uniqNums[i]*2+uniqNums[j]+uniqNums[k] == target) && counter[uniqNums[i]] > 1 {
res = append(res, []int{uniqNums[i], uniqNums[i], uniqNums[j], uniqNums[k]})
}
if (uniqNums[j]*2+uniqNums[i]+uniqNums[k] == target) && counter[uniqNums[j]] > 1 {
res = append(res, []int{uniqNums[i], uniqNums[j], uniqNums[j], uniqNums[k]})
}
if (uniqNums[k]*2+uniqNums[i]+uniqNums[j] == target) && counter[uniqNums[k]] > 1 {
res = append(res, []int{uniqNums[i], uniqNums[j], uniqNums[k], uniqNums[k]})
}
c := target - uniqNums[i] - uniqNums[j] - uniqNums[k]
if c > uniqNums[k] && counter[c] > 0 {
res = append(res, []int{uniqNums[i], uniqNums[j], uniqNums[k], c})
}
}
}
}
return res
}