Skip to content

Commit 7d7dddb

Browse files
committed
3sum
1 parent 70499d4 commit 7d7dddb

File tree

2 files changed

+218
-0
lines changed

2 files changed

+218
-0
lines changed

15.3_sum/sum.go

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
// 15. 3Sum
2+
// Topics: 'Array', 'Sorting', 'Two Pointers'
3+
// Level: 'Medium'
4+
5+
// Given an integer array nums, return all the triplets [nums[i], nums[j], nums[k]] such that i != j, i != k, and j != k, and nums[i] + nums[j] + nums[k] == 0.
6+
7+
// Notice that the solution set must not contain duplicate triplets.
8+
9+
// Example 1:
10+
11+
// Input: nums = [-1,0,1,2,-1,-4]
12+
// Output: [[-1,-1,2],[-1,0,1]]
13+
// Explanation:
14+
// nums[0] + nums[1] + nums[2] = (-1) + 0 + 1 = 0.
15+
// nums[1] + nums[2] + nums[4] = 0 + 1 + (-1) = 0.
16+
// nums[0] + nums[3] + nums[4] = (-1) + 2 + (-1) = 0.
17+
// The distinct triplets are [-1,0,1] and [-1,-1,2].
18+
// Notice that the order of the output and the order of the triplets does not matter.
19+
20+
// Example 2:
21+
22+
// Input: nums = [0,1,1]
23+
// Output: []
24+
// Explanation: The only possible triplet does not sum up to 0.
25+
26+
// Example 3:
27+
28+
// Input: nums = [0,0,0]
29+
// Output: [[0,0,0]]
30+
// Explanation: The only possible triplet sums up to 0.
31+
32+
// Constraints:
33+
34+
// 3 <= nums.length <= 3000
35+
// -105 <= nums[i] <= 105
36+
37+
package sum
38+
39+
import "sort"
40+
41+
func threeSum(nums []int) [][]int {
42+
sort.Ints(nums)
43+
res := [][]int{}
44+
45+
for i := range len(nums) - 2 {
46+
if i > 0 && nums[i-1] == nums[i] {
47+
continue
48+
}
49+
50+
L := i + 1
51+
R := len(nums) - 1
52+
53+
for L < R {
54+
total := nums[i] + nums[L] + nums[R]
55+
if total == 0 {
56+
res = append(res, []int{nums[i], nums[L], nums[R]})
57+
for L < R && nums[R] == nums[R-1] {
58+
R--
59+
}
60+
for L < R && nums[L] == nums[L+1] {
61+
L++
62+
}
63+
R--
64+
L++
65+
} else if total > 0 {
66+
R--
67+
} else {
68+
L++
69+
}
70+
}
71+
}
72+
return res
73+
}

15.3_sum/sum_test.go

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
package sum
2+
3+
import (
4+
"reflect"
5+
"sort"
6+
"testing"
7+
)
8+
9+
// Helper function to sort triplets for comparison
10+
func sortTriplets(triplets [][]int) {
11+
// Sort each individual triplet
12+
for _, triplet := range triplets {
13+
sort.Ints(triplet)
14+
}
15+
// Sort the slice of triplets
16+
sort.Slice(triplets, func(i, j int) bool {
17+
for k := 0; k < 3; k++ {
18+
if triplets[i][k] != triplets[j][k] {
19+
return triplets[i][k] < triplets[j][k]
20+
}
21+
}
22+
return false
23+
})
24+
}
25+
26+
func TestThreeSum(t *testing.T) {
27+
tests := []struct {
28+
name string
29+
nums []int
30+
expected [][]int
31+
}{
32+
{
33+
name: "Example 1 - Multiple triplets",
34+
nums: []int{-1, 0, 1, 2, -1, -4},
35+
expected: [][]int{{-1, -1, 2}, {-1, 0, 1}},
36+
},
37+
{
38+
name: "Example 2 - No valid triplets",
39+
nums: []int{0, 1, 1},
40+
expected: [][]int{},
41+
},
42+
{
43+
name: "Example 3 - All zeros",
44+
nums: []int{0, 0, 0},
45+
expected: [][]int{{0, 0, 0}},
46+
},
47+
{
48+
name: "Minimum length array",
49+
nums: []int{-1, 0, 1},
50+
expected: [][]int{{-1, 0, 1}},
51+
},
52+
{
53+
name: "All negative numbers",
54+
nums: []int{-5, -4, -3, -2, -1},
55+
expected: [][]int{},
56+
},
57+
{
58+
name: "All positive numbers",
59+
nums: []int{1, 2, 3, 4, 5},
60+
expected: [][]int{},
61+
},
62+
{
63+
name: "Multiple duplicates",
64+
nums: []int{-2, 0, 0, 2, 2},
65+
expected: [][]int{{-2, 0, 2}},
66+
},
67+
{
68+
name: "Complex case with duplicates",
69+
nums: []int{-4, -2, -2, -2, 0, 1, 2, 2, 2, 3, 3, 4, 4, 6, 6},
70+
expected: [][]int{{-4, -2, 6}, {-4, 0, 4}, {-4, 1, 3}, {-4, 2, 2}, {-2, -2, 4}, {-2, 0, 2}},
71+
},
72+
{
73+
name: "Two zeros with negatives and positives",
74+
nums: []int{-1, 0, 0, 1},
75+
expected: [][]int{{-1, 0, 1}},
76+
},
77+
{
78+
name: "Single triplet with duplicates",
79+
nums: []int{1, 1, -2},
80+
expected: [][]int{{-2, 1, 1}},
81+
},
82+
{
83+
name: "Multiple zeros",
84+
nums: []int{0, 0, 0, 0},
85+
expected: [][]int{{0, 0, 0}},
86+
},
87+
{
88+
name: "Empty result with exact 3 elements",
89+
nums: []int{1, 2, 3},
90+
expected: [][]int{},
91+
},
92+
}
93+
94+
for _, tt := range tests {
95+
t.Run(tt.name, func(t *testing.T) {
96+
// Make a copy to avoid modifying test data
97+
numsCopy := make([]int, len(tt.nums))
98+
copy(numsCopy, tt.nums)
99+
100+
result := threeSum(numsCopy)
101+
102+
// Sort both result and expected for comparison
103+
sortTriplets(result)
104+
sortTriplets(tt.expected)
105+
106+
if !reflect.DeepEqual(result, tt.expected) {
107+
t.Errorf("threeSum(%v) = %v, want %v", tt.nums, result, tt.expected)
108+
}
109+
})
110+
}
111+
}
112+
113+
func TestThreeSumNoDuplicates(t *testing.T) {
114+
nums := []int{-1, 0, 1, 2, -1, -4}
115+
result := threeSum(nums)
116+
117+
// Check for duplicate triplets
118+
seen := make(map[[3]int]bool)
119+
for _, triplet := range result {
120+
sort.Ints(triplet)
121+
key := [3]int{triplet[0], triplet[1], triplet[2]}
122+
if seen[key] {
123+
t.Errorf("Found duplicate triplet: %v", triplet)
124+
}
125+
seen[key] = true
126+
}
127+
}
128+
129+
func TestThreeSumValidSums(t *testing.T) {
130+
testCases := [][]int{
131+
{-1, 0, 1, 2, -1, -4},
132+
{0, 0, 0},
133+
{-2, 0, 1, 1, 2},
134+
}
135+
136+
for _, nums := range testCases {
137+
result := threeSum(nums)
138+
for _, triplet := range result {
139+
sum := triplet[0] + triplet[1] + triplet[2]
140+
if sum != 0 {
141+
t.Errorf("Triplet %v does not sum to 0, got sum = %d", triplet, sum)
142+
}
143+
}
144+
}
145+
}

0 commit comments

Comments
 (0)