Skip to content

Commit 15abdc5

Browse files
authored
Merge pull request #188 from BrianLusina/feat/algorithms-three-number-sum
feat(algorithms, two-pointers): three number sum
2 parents 366ce25 + b15e349 commit 15abdc5

File tree

5 files changed

+98
-2
lines changed

5 files changed

+98
-2
lines changed

algorithms/arrays/remove_duplicates/README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,9 @@ and 4 respectively. It doesn't matter what values are set beyond the returned le
3939

4040
Constraints:
4141

42-
0 <= nums.length <= 3 * 104 -104 <= nums[i] <= 104 nums is sorted in ascending order.
42+
- 0 <= nums.length <= 3 * 10^4
43+
- -10^4 <= nums[i] <= 10^4
44+
- nums is sorted in ascending order.
4345

4446
---
4547

algorithms/arrays/remove_duplicates/__init__.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,13 +38,19 @@ def remove_duplicates_from_sorted_list(nums: List[int]) -> int:
3838
if not nums:
3939
return 0
4040

41+
# i tracks the position where the next unique element should be placed.
42+
# Start j from index 1 and iterate through the list to find unique elements.
4143
i, j = 0, 1
4244

4345
while j < len(nums):
46+
# If the current element is different from the last unique element found:
4447
if nums[i] != nums[j]:
48+
# Move i forward to store the new unique element
4549
i += 1
50+
# Place the unique element in its correct position
4651
nums[i] = nums[j]
4752
j += 1
53+
# Return the count of unique elements (index i + 1 because i is zero-based)
4854
return i + 1
4955

5056

algorithms/two_pointers/three_sum/README.md

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,3 +90,44 @@ at least 2 more elements after i for left and right to form a triplet.
9090

9191
![Solution 18](./images/solutions/three_sum_solution_18.png)
9292
![Solution 19](./images/solutions/three_sum_solution_19.png)
93+
94+
---
95+
96+
# Three Number Sum
97+
98+
Write a function that takes in a non-empty array of distinct integers and an
99+
integer representing a target sum. The function should find all triplets in
100+
the array that sum up to the target sum and return a two-dimensional array of
101+
all these triplets. The numbers in each triplet should be ordered in ascending
102+
order, and the triplets themselves should be ordered in ascending order with
103+
respect to the numbers they hold.
104+
105+
If no three numbers sum up to the target sum, the function should return an
106+
empty array.
107+
108+
## Examples
109+
110+
Example 1
111+
112+
```text
113+
array = [12, 3, 1, 2, -6, 5, -8, 6]
114+
targetSum = 0
115+
[[-8, 2, 6], [-8, 3, 5], [-6, 1, 5]]
116+
```
117+
118+
## Hints
119+
120+
- Using three for loops to calculate the sums of all possible triplets in the array would generate an algorithm that runs
121+
in O(n^3) time, where n is the length of the input array. Can you come up with something faster using only two for loops?
122+
- Try sorting the array and traversing it once. At each number, place a left pointer on the number immediately to the
123+
right of your current number and a right pointer on the final number in the array. Check if the current number, the
124+
left number, and the right number sum up to the target sum. How can you proceed from there, remembering the fact that
125+
you sorted the array?
126+
- Since the array is now sorted (see Hint #2), you know that moving the left pointer mentioned in Hint #2 one place to
127+
the right will lead to a greater left number and thus a greater sum. Similarly, you know that moving the right pointer
128+
one place to the left will lead to a smaller right number and thus a smaller sum. This means that, depending on the
129+
size of each triplet's (current number, left number, right number) sum relative to the target sum, you should either
130+
move the left pointer, the right pointer, or both to obtain a potentially valid triplet.
131+
132+
> The solution here will be the same as the Three Sum problem above, with the only difference being the targetSum is not
133+
> 0 and has been provided as an input

algorithms/two_pointers/three_sum/__init__.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,3 +49,37 @@ def three_sum(nums: List[int]) -> List[List[int]]:
4949
right -= 1
5050

5151
return result
52+
53+
54+
def three_number_sum(array: List[int], target_sum: int) -> List[List[int]]:
55+
if not array:
56+
return []
57+
58+
# Sort the array and store the result in nums to avoid side effects to the caller of this
59+
# function. This incurs a a time complexity of O(n (log(n))) and a space complexity of O(n)
60+
# where n is the number of elements in the array
61+
nums = sorted(array)
62+
n = len(nums)
63+
result = []
64+
65+
for idx, num in enumerate(nums):
66+
left = idx + 1
67+
right = n - 1
68+
69+
while left < right:
70+
total = num + nums[left] + nums[right]
71+
if total == target_sum:
72+
result.append([num, nums[left], nums[right]])
73+
# move the left pointer to avoid duplicates while it is still less than the right
74+
while left < right and nums[left] == nums[left + 1]:
75+
left += 1
76+
while left < right and nums[right] == nums[right - 1]:
77+
right -= 1
78+
left += 1
79+
right -= 1
80+
elif total > target_sum:
81+
right -= 1
82+
else:
83+
left += 1
84+
85+
return result

algorithms/two_pointers/three_sum/test_three_sum.py

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import unittest
22
from typing import List
33
from parameterized import parameterized
4-
from algorithms.two_pointers.three_sum import three_sum
4+
from algorithms.two_pointers.three_sum import three_sum, three_number_sum
55

66
THREE_SUM_TEST_CASES = [
77
([-1, 0, 1, 2, -1, -4], [[-1, -1, 2], [-1, 0, 1]]),
@@ -14,6 +14,10 @@
1414
([-1, 0, 1, 2, -1, -4, -1, 2, 1], [[-1, -1, 2], [-1, 0, 1], [-4, 2, 2]]),
1515
]
1616

17+
THREE_NUMBER_SUM_TEST_CASES = [
18+
([12, 3, 1, 2, -6, 5, -8, 6], 0, [[-8, 2, 6], [-8, 3, 5], [-6, 1, 5]]),
19+
]
20+
1721

1822
class ThreeSumTestCases(unittest.TestCase):
1923
@parameterized.expand(THREE_SUM_TEST_CASES)
@@ -22,5 +26,14 @@ def test_three_sum(self, nums: List[int], expected: List[List[int]]):
2226
self.assertEqual(expected, actual)
2327

2428

29+
class ThreeNumberSumTestCases(unittest.TestCase):
30+
@parameterized.expand(THREE_NUMBER_SUM_TEST_CASES)
31+
def test_three_number_sum(
32+
self, nums: List[int], target_sum: int, expected: List[List[int]]
33+
):
34+
actual = three_number_sum(nums, target_sum)
35+
self.assertEqual(expected, actual)
36+
37+
2538
if __name__ == "__main__":
2639
unittest.main()

0 commit comments

Comments
 (0)