Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
105 changes: 105 additions & 0 deletions 2_two_pointers/3_3sum/solution.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
package sum

import (
"sort"
)

// Time complexity: O(n^3)
// Space complexity: O(n)
func ThreeSumBruteForce(nums []int) [][]int {
n := len(nums)
result := map[[3]int]bool{}

for i := 0; i < n; i++ {
for j := i + 1; j < n; j++ {
for k := j + 1; k < n; k++ {
if nums[i]+nums[j]+nums[k] == 0 {
triplet := []int{nums[i], nums[j], nums[k]}
sort.Ints(triplet) // to avoid duplicate triplets in different orders
var key [3]int
copy(key[:], triplet)
result[key] = true
}
}
}
}

// convert map to slice - initialize empty slice to avoid nil
output := make([][]int, 0)
for k := range result {
output = append(output, []int{k[0], k[1], k[2]})
}
return output
}

// Time complexity: O(n^2)
// Space complexity: O(n)
func ThreeSumHashMap(nums []int) [][]int {
n := len(nums)
result := map[[3]int]bool{}

for i := 0; i < n; i++ {
target := -nums[i]
seen := map[int]bool{}
for j := i + 1; j < n; j++ {
complement := target - nums[j]
if seen[complement] {
triplet := []int{nums[i], nums[j], complement}
sort.Ints(triplet)
var key [3]int
copy(key[:], triplet)
result[key] = true
}
seen[nums[j]] = true
}
}

// convert map to slice - initialize empty slice to avoid nil
output := make([][]int, 0)
for k := range result {
output = append(output, []int{k[0], k[1], k[2]})
}
return output
}

// Time complexity: O(n^2)
// Space complexity: O(1)
func ThreeSumTwoPointers(nums []int) [][]int {
sort.Ints(nums)
n := len(nums)
result := make([][]int, 0) // Initialize empty slice to avoid nil

for i := 0; i < n-2; i++ {
// Skip duplicates
if i > 0 && nums[i] == nums[i-1] {
continue
}
left := i + 1
right := n - 1

for left < right {
sum := nums[i] + nums[left] + nums[right]

if sum == 0 {
result = append(result, []int{nums[i], nums[left], nums[right]})

// Skip duplicates
for left < right && nums[left] == nums[left+1] {
left++
}
for left < right && nums[right] == nums[right-1] {
right--
}

left++
right--
} else if sum < 0 {
left++
} else {
right--
}
}
}

return result
}
67 changes: 67 additions & 0 deletions 2_two_pointers/3_3sum/solution_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package sum

import (
"reflect"
"sort"
"testing"
)

func sortTriplets(triplets [][]int) {
for _, triplet := range triplets {
sort.Ints(triplet)
}
sort.Slice(triplets, func(i, j int) bool {
for k := 0; k < 3; k++ {
if triplets[i][k] != triplets[j][k] {
return triplets[i][k] < triplets[j][k]
}
}
return false
})
}

func TestThreeSum(t *testing.T) {
cases := []struct {
name string
fn func([]int) [][]int
nums []int
expected [][]int
}{
// ThreeSumBruteForce
{"Brute Force - Typical case with multiple triplets", ThreeSumBruteForce, []int{-1, 0, 1, 2, -1, -4}, [][]int{{-1, -1, 2}, {-1, 0, 1}}},
{"Brute Force - All zeroes", ThreeSumBruteForce, []int{0, 0, 0, 0}, [][]int{{0, 0, 0}}},
{"Brute Force - No valid triplet", ThreeSumBruteForce, []int{1, 2, -2, -1}, [][]int{}},
{"Brute Force - Empty array", ThreeSumBruteForce, []int{}, [][]int{}},
{"Brute Force - Single element", ThreeSumBruteForce, []int{1}, [][]int{}},
{"Brute Force - Two elements", ThreeSumBruteForce, []int{1, 2}, [][]int{}},

// HashMap
{"Hash Map - Typical case with multiple triplets", ThreeSumHashMap, []int{-1, 0, 1, 2, -1, -4}, [][]int{{-1, -1, 2}, {-1, 0, 1}}},
{"Hash Map - All zeroes", ThreeSumHashMap, []int{0, 0, 0, 0}, [][]int{{0, 0, 0}}},
{"Hash Map - No valid triplet", ThreeSumHashMap, []int{1, 2, -2, -1}, [][]int{}},
{"Hash Map - Empty array", ThreeSumHashMap, []int{}, [][]int{}},
{"Hash Map - Single element", ThreeSumHashMap, []int{1}, [][]int{}},
{"Hash Map - Two elements", ThreeSumHashMap, []int{1, 2}, [][]int{}},

// TwoPointers
{"Two Pointers - Typical case with multiple triplets", ThreeSumTwoPointers, []int{-1, 0, 1, 2, -1, -4}, [][]int{{-1, -1, 2}, {-1, 0, 1}}},
{"Two Pointers - All zeroes", ThreeSumTwoPointers, []int{0, 0, 0, 0}, [][]int{{0, 0, 0}}},
{"Two Pointers - No valid triplet", ThreeSumTwoPointers, []int{1, 2, -2, -1}, [][]int{}},
{"Two Pointers - Empty array", ThreeSumTwoPointers, []int{}, [][]int{}},
{"Two Pointers - Single element", ThreeSumTwoPointers, []int{1}, [][]int{}},
{"Two Pointers - Two elements", ThreeSumTwoPointers, []int{1, 2}, [][]int{}},
{"Two Pointers - Large numbers", ThreeSumTwoPointers, []int{-4, -1, -1, 0, 1, 2}, [][]int{{-1, -1, 2}, {-1, 0, 1}}},
{"Two Pointers - Duplicates with valid triplet", ThreeSumTwoPointers, []int{-2, 0, 0, 2, 2}, [][]int{{-2, 0, 2}}},
}

for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
got := c.fn(c.nums)
sortTriplets(got)
sortTriplets(c.expected)
if !reflect.DeepEqual(got, c.expected) {
t.Errorf("%s failed: expected %v, got %v", c.name, c.expected, got)
}
})
}
}
Loading