Skip to content

Commit cac2504

Browse files
committed
4sum
1 parent 7d7dddb commit cac2504

File tree

2 files changed

+229
-0
lines changed

2 files changed

+229
-0
lines changed

18.4sum/sum.go

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
// 18. 4Sum
2+
// Topics: 'Array', 'Two Pointers', 'Sorting'
3+
// Level: 'Medium'
4+
5+
// Given an array nums of n integers, return an array of all the unique quadruplets [nums[a], nums[b], nums[c], nums[d]] such that:
6+
7+
// 0 <= a, b, c, d < n
8+
// a, b, c, and d are distinct.
9+
// nums[a] + nums[b] + nums[c] + nums[d] == target
10+
11+
// You may return the answer in any order.
12+
13+
// Example 1:
14+
15+
// Input: nums = [1,0,-1,0,-2,2], target = 0
16+
// Output: [[-2,-1,1,2],[-2,0,0,2],[-1,0,0,1]]
17+
18+
// Example 2:
19+
20+
// Input: nums = [2,2,2,2,2], target = 8
21+
// Output: [[2,2,2,2]]
22+
23+
// Constraints:
24+
25+
// 1 <= nums.length <= 200
26+
// -109 <= nums[i] <= 109
27+
// -109 <= target <= 109
28+
29+
package sum
30+
31+
import "sort"
32+
33+
func fourSum(nums []int, target int) [][]int {
34+
sort.Ints(nums)
35+
res := [][]int{}
36+
37+
for i := range len(nums) - 3 {
38+
if i > 0 && nums[i-1] == nums[i] {
39+
continue
40+
}
41+
for L := i + 1; L < len(nums)-2; L++ {
42+
if L > i+1 && nums[L] == nums[L-1] {
43+
continue
44+
}
45+
L1 := L + 1
46+
R := len(nums) - 1
47+
for L1 < R {
48+
total := nums[i] + nums[L] + nums[L1] + nums[R]
49+
if total == target {
50+
res = append(res, []int{nums[i], nums[L], nums[L1], nums[R]})
51+
for L1 < R && nums[L1] == nums[L1+1] {
52+
L1++
53+
}
54+
for L1 < R && nums[R] == nums[R-1] {
55+
R--
56+
}
57+
L1++
58+
R--
59+
} else if total > target {
60+
R--
61+
} else {
62+
L1++
63+
}
64+
}
65+
}
66+
}
67+
return res
68+
}

18.4sum/sum_test.go

Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
package sum
2+
3+
import (
4+
"reflect"
5+
"sort"
6+
"testing"
7+
)
8+
9+
func TestFourSum(t *testing.T) {
10+
tests := []struct {
11+
name string
12+
nums []int
13+
target int
14+
want [][]int
15+
}{
16+
{
17+
name: "basic case",
18+
nums: []int{1, 0, -1, 0, -2, 2},
19+
target: 0,
20+
want: [][]int{{-2, -1, 1, 2}, {-2, 0, 0, 2}, {-1, 0, 0, 1}},
21+
},
22+
{
23+
name: "no solution",
24+
nums: []int{2, 2, 2, 2, 2},
25+
target: 9,
26+
want: [][]int{},
27+
},
28+
{
29+
name: "single solution",
30+
nums: []int{1, 2, 3, 4},
31+
target: 10,
32+
want: [][]int{{1, 2, 3, 4}},
33+
},
34+
{
35+
name: "empty array",
36+
nums: []int{},
37+
target: 0,
38+
want: [][]int{},
39+
},
40+
{
41+
name: "array too small",
42+
nums: []int{1, 2, 3},
43+
target: 6,
44+
want: [][]int{},
45+
},
46+
{
47+
name: "all same values with solution",
48+
nums: []int{2, 2, 2, 2, 2},
49+
target: 8,
50+
want: [][]int{{2, 2, 2, 2}},
51+
},
52+
{
53+
name: "duplicates test",
54+
nums: []int{0, 0, 0, 0},
55+
target: 0,
56+
want: [][]int{{0, 0, 0, 0}},
57+
},
58+
{
59+
name: "negative target",
60+
nums: []int{-3, -2, -1, 0, 1, 2, 3},
61+
target: -6,
62+
want: [][]int{{-3, -2, -1, 0}},
63+
},
64+
{
65+
name: "positive target",
66+
nums: []int{-3, -2, -1, 0, 1, 2, 3},
67+
target: 6,
68+
want: [][]int{{0, 1, 2, 3}},
69+
},
70+
{
71+
name: "large target",
72+
nums: []int{1, 2, 3, 4, 5, 6},
73+
target: 18,
74+
want: [][]int{{3, 4, 5, 6}},
75+
},
76+
{
77+
name: "multiple duplicates",
78+
nums: []int{-1, -1, -1, 0, 0, 0, 1, 1, 1},
79+
target: 0,
80+
want: [][]int{{-1, -1, 1, 1}, {-1, 0, 0, 1}},
81+
},
82+
{
83+
name: "complex duplicates",
84+
nums: []int{2, 2, 2, 2, 2, 2, 2, 2},
85+
target: 8,
86+
want: [][]int{{2, 2, 2, 2}},
87+
},
88+
{
89+
name: "exactly 4 elements",
90+
nums: []int{1, 2, 3, 4},
91+
target: 100,
92+
want: [][]int{},
93+
},
94+
{
95+
name: "exactly 4 elements with solution",
96+
nums: []int{1, 2, 3, 4},
97+
target: 10,
98+
want: [][]int{{1, 2, 3, 4}},
99+
},
100+
}
101+
102+
for _, tt := range tests {
103+
t.Run(tt.name, func(t *testing.T) {
104+
got := fourSum(tt.nums, tt.target)
105+
106+
// Sort both slices for comparison
107+
sortQuadruplets(got)
108+
sortQuadruplets(tt.want)
109+
110+
if !reflect.DeepEqual(got, tt.want) {
111+
t.Errorf("fourSum() = %v, want %v", got, tt.want)
112+
}
113+
})
114+
}
115+
}
116+
117+
// Helper function to sort quadruplets for consistent comparison
118+
func sortQuadruplets(quads [][]int) {
119+
// Sort each individual quadruplet
120+
for _, quad := range quads {
121+
sort.Ints(quad)
122+
}
123+
124+
// Sort the slice of quadruplets
125+
sort.Slice(quads, func(i, j int) bool {
126+
for k := 0; k < 4 && k < len(quads[i]) && k < len(quads[j]); k++ {
127+
if quads[i][k] != quads[j][k] {
128+
return quads[i][k] < quads[j][k]
129+
}
130+
}
131+
return false
132+
})
133+
}
134+
135+
// Test for no duplicates in results
136+
func TestFourSumNoDuplicates(t *testing.T) {
137+
nums := []int{1, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3}
138+
target := 8
139+
140+
result := fourSum(nums, target)
141+
142+
// Check for duplicate quadruplets
143+
seen := make(map[[4]int]bool)
144+
for _, quad := range result {
145+
if len(quad) != 4 {
146+
t.Errorf("Expected quadruplet of length 4, got %d", len(quad))
147+
}
148+
149+
key := [4]int{quad[0], quad[1], quad[2], quad[3]}
150+
if seen[key] {
151+
t.Errorf("Found duplicate quadruplet: %v", quad)
152+
}
153+
seen[key] = true
154+
155+
// Verify sum
156+
sum := quad[0] + quad[1] + quad[2] + quad[3]
157+
if sum != target {
158+
t.Errorf("Quadruplet %v sums to %d, expected %d", quad, sum, target)
159+
}
160+
}
161+
}

0 commit comments

Comments
 (0)