diff --git a/algorithms/arrays/remove_duplicates/README.md b/algorithms/arrays/remove_duplicates/README.md index c752fd9b..c4a05106 100644 --- a/algorithms/arrays/remove_duplicates/README.md +++ b/algorithms/arrays/remove_duplicates/README.md @@ -39,7 +39,9 @@ and 4 respectively. It doesn't matter what values are set beyond the returned le Constraints: -0 <= nums.length <= 3 * 104 -104 <= nums[i] <= 104 nums is sorted in ascending order. +- 0 <= nums.length <= 3 * 10^4 +- -10^4 <= nums[i] <= 10^4 +- nums is sorted in ascending order. --- diff --git a/algorithms/arrays/remove_duplicates/__init__.py b/algorithms/arrays/remove_duplicates/__init__.py index ec7af065..a743a91d 100644 --- a/algorithms/arrays/remove_duplicates/__init__.py +++ b/algorithms/arrays/remove_duplicates/__init__.py @@ -38,13 +38,19 @@ def remove_duplicates_from_sorted_list(nums: List[int]) -> int: if not nums: return 0 + # i tracks the position where the next unique element should be placed. + # Start j from index 1 and iterate through the list to find unique elements. i, j = 0, 1 while j < len(nums): + # If the current element is different from the last unique element found: if nums[i] != nums[j]: + # Move i forward to store the new unique element i += 1 + # Place the unique element in its correct position nums[i] = nums[j] j += 1 + # Return the count of unique elements (index i + 1 because i is zero-based) return i + 1 diff --git a/algorithms/two_pointers/three_sum/README.md b/algorithms/two_pointers/three_sum/README.md index 50c28079..a62b2ae8 100644 --- a/algorithms/two_pointers/three_sum/README.md +++ b/algorithms/two_pointers/three_sum/README.md @@ -90,3 +90,44 @@ at least 2 more elements after i for left and right to form a triplet. ![Solution 18](./images/solutions/three_sum_solution_18.png) ![Solution 19](./images/solutions/three_sum_solution_19.png) + +--- + +# Three Number Sum + +Write a function that takes in a non-empty array of distinct integers and an +integer representing a target sum. The function should find all triplets in +the array that sum up to the target sum and return a two-dimensional array of +all these triplets. The numbers in each triplet should be ordered in ascending +order, and the triplets themselves should be ordered in ascending order with +respect to the numbers they hold. + +If no three numbers sum up to the target sum, the function should return an +empty array. + +## Examples + +Example 1 + +```text +array = [12, 3, 1, 2, -6, 5, -8, 6] +targetSum = 0 +[[-8, 2, 6], [-8, 3, 5], [-6, 1, 5]] +``` + +## Hints + +- Using three for loops to calculate the sums of all possible triplets in the array would generate an algorithm that runs + 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? +- Try sorting the array and traversing it once. At each number, place a left pointer on the number immediately to the + right of your current number and a right pointer on the final number in the array. Check if the current number, the + left number, and the right number sum up to the target sum. How can you proceed from there, remembering the fact that + you sorted the array? +- Since the array is now sorted (see Hint #2), you know that moving the left pointer mentioned in Hint #2 one place to + the right will lead to a greater left number and thus a greater sum. Similarly, you know that moving the right pointer + one place to the left will lead to a smaller right number and thus a smaller sum. This means that, depending on the + size of each triplet's (current number, left number, right number) sum relative to the target sum, you should either + move the left pointer, the right pointer, or both to obtain a potentially valid triplet. + +> The solution here will be the same as the Three Sum problem above, with the only difference being the targetSum is not +> 0 and has been provided as an input \ No newline at end of file diff --git a/algorithms/two_pointers/three_sum/__init__.py b/algorithms/two_pointers/three_sum/__init__.py index 012cc0fc..ba654e12 100644 --- a/algorithms/two_pointers/three_sum/__init__.py +++ b/algorithms/two_pointers/three_sum/__init__.py @@ -49,3 +49,37 @@ def three_sum(nums: List[int]) -> List[List[int]]: right -= 1 return result + + +def three_number_sum(array: List[int], target_sum: int) -> List[List[int]]: + if not array: + return [] + + # Sort the array and store the result in nums to avoid side effects to the caller of this + # function. This incurs a a time complexity of O(n (log(n))) and a space complexity of O(n) + # where n is the number of elements in the array + nums = sorted(array) + n = len(nums) + result = [] + + for idx, num in enumerate(nums): + left = idx + 1 + right = n - 1 + + while left < right: + total = num + nums[left] + nums[right] + if total == target_sum: + result.append([num, nums[left], nums[right]]) + # move the left pointer to avoid duplicates while it is still less than the right + while left < right and nums[left] == nums[left + 1]: + left += 1 + while left < right and nums[right] == nums[right - 1]: + right -= 1 + left += 1 + right -= 1 + elif total > target_sum: + right -= 1 + else: + left += 1 + + return result diff --git a/algorithms/two_pointers/three_sum/test_three_sum.py b/algorithms/two_pointers/three_sum/test_three_sum.py index f2b41b8c..61376ce4 100644 --- a/algorithms/two_pointers/three_sum/test_three_sum.py +++ b/algorithms/two_pointers/three_sum/test_three_sum.py @@ -1,7 +1,7 @@ import unittest from typing import List from parameterized import parameterized -from algorithms.two_pointers.three_sum import three_sum +from algorithms.two_pointers.three_sum import three_sum, three_number_sum THREE_SUM_TEST_CASES = [ ([-1, 0, 1, 2, -1, -4], [[-1, -1, 2], [-1, 0, 1]]), @@ -14,6 +14,10 @@ ([-1, 0, 1, 2, -1, -4, -1, 2, 1], [[-1, -1, 2], [-1, 0, 1], [-4, 2, 2]]), ] +THREE_NUMBER_SUM_TEST_CASES = [ + ([12, 3, 1, 2, -6, 5, -8, 6], 0, [[-8, 2, 6], [-8, 3, 5], [-6, 1, 5]]), +] + class ThreeSumTestCases(unittest.TestCase): @parameterized.expand(THREE_SUM_TEST_CASES) @@ -22,5 +26,14 @@ def test_three_sum(self, nums: List[int], expected: List[List[int]]): self.assertEqual(expected, actual) +class ThreeNumberSumTestCases(unittest.TestCase): + @parameterized.expand(THREE_NUMBER_SUM_TEST_CASES) + def test_three_number_sum( + self, nums: List[int], target_sum: int, expected: List[List[int]] + ): + actual = three_number_sum(nums, target_sum) + self.assertEqual(expected, actual) + + if __name__ == "__main__": unittest.main()