diff --git a/DIRECTORY.md b/DIRECTORY.md index 3aabf9af..fa59b1d8 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -19,10 +19,6 @@ * [Test Sorted Squared Array](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/arrays/sorted_squared_array/test_sorted_squared_array.py) * Subsequence * [Test Is Valid Subsequence](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/arrays/subsequence/test_is_valid_subsequence.py) - * Two Sum - * [Test Two Sum](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/arrays/two_sum/test_two_sum.py) - * Two Sum Less K - * [Test Two Sum](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/arrays/two_sum_less_k/test_two_sum.py) * Backtracking * Combination * [Test Combination 2](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/backtracking/combination/test_combination_2.py) @@ -210,20 +206,32 @@ * Taxi Numbers * [Taxi Numbers](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/taxi_numbers/taxi_numbers.py) * Two Pointers + * Array 3 Pointers + * [Test Array 3 Pointers](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/two_pointers/array_3_pointers/test_array_3_pointers.py) * Find Sum Of Three * [Test Find Sum Of Three](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/two_pointers/find_sum_of_three/test_find_sum_of_three.py) * Merge Sorted Arrays * [Test Merge Sorted Arrays](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/two_pointers/merge_sorted_arrays/test_merge_sorted_arrays.py) + * Move Zeroes + * [Test Move Zeroes](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/two_pointers/move_zeroes/test_move_zeroes.py) * Next Permutation * [Test Next Permutation](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/two_pointers/next_permutation/test_next_permutation.py) * Pair With Sum In Array * [Test Pair With Sum In Array](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/two_pointers/pair_with_sum_in_array/test_pair_with_sum_in_array.py) + * Rain Water Trapped + * [Test Trapped Rain Water](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/two_pointers/rain_water_trapped/test_trapped_rain_water.py) * Reverse Array * [Test Reverse Array](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/two_pointers/reverse_array/test_reverse_array.py) * Sort Colors * [Test Sort Colors](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/two_pointers/sort_colors/test_sort_colors.py) * Three Sum * [Test Three Sum](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/two_pointers/three_sum/test_three_sum.py) + * Triangle Numbers + * [Test Triangle Numbers](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/two_pointers/triangle_numbers/test_triangle_numbers.py) + * Two Sum + * [Test Two Sum](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/two_pointers/two_sum/test_two_sum.py) + * Two Sum Less K + * [Test Two Sum](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/two_pointers/two_sum_less_k/test_two_sum.py) * Unique Bsts * [Unique Bsts](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/unique_bsts/unique_bsts.py) * Word Count @@ -577,8 +585,6 @@ * Allergies * [Test Allergies](https://github.com/BrianLusina/PythonSnips/blob/master/puzzles/allergies/test_allergies.py) * Arrays - * Array 3 Pointers - * [Test Array 3 Pointers](https://github.com/BrianLusina/PythonSnips/blob/master/puzzles/arrays/array_3_pointers/test_array_3_pointers.py) * Can Place Flowers * [Test Can Place Flowers](https://github.com/BrianLusina/PythonSnips/blob/master/puzzles/arrays/can_place_flowers/test_can_place_flowers.py) * Candy @@ -605,12 +611,8 @@ * [Test Max Average Subarray](https://github.com/BrianLusina/PythonSnips/blob/master/puzzles/arrays/maximum_average_subarray/test_max_average_subarray.py) * Maxlen Contiguous Binary Subarray * [Test Maxlen Contiguous Binary Subarray](https://github.com/BrianLusina/PythonSnips/blob/master/puzzles/arrays/maxlen_contiguous_binary_subarray/test_maxlen_contiguous_binary_subarray.py) - * Move Zeroes - * [Test Move Zeroes](https://github.com/BrianLusina/PythonSnips/blob/master/puzzles/arrays/move_zeroes/test_move_zeroes.py) * Product Of Array Except Self * [Test Product Except Self](https://github.com/BrianLusina/PythonSnips/blob/master/puzzles/arrays/product_of_array_except_self/test_product_except_self.py) - * Rain Water Trapped - * [Test Trapped Rain Water](https://github.com/BrianLusina/PythonSnips/blob/master/puzzles/arrays/rain_water_trapped/test_trapped_rain_water.py) * Rotation * Cyclic Rotation * [Test Cyclic Rotation](https://github.com/BrianLusina/PythonSnips/blob/master/puzzles/arrays/rotation/cyclic_rotation/test_cyclic_rotation.py) diff --git a/algorithms/intervals/insert_interval/README.md b/algorithms/intervals/insert_interval/README.md index 2694b935..080dd229 100644 --- a/algorithms/intervals/insert_interval/README.md +++ b/algorithms/intervals/insert_interval/README.md @@ -25,17 +25,17 @@ Return the updated list of intervals. ## Solution -We first want to create a new list merged to store the merged intervals we will return at the end. +We first want to create a new list `merged` to store the merged intervals we will return at the end. This solution operates in 3 phases: -1. Add all the intervals ending before newInterval starts to merged. -2. Merge all overlapping intervals with newInterval and add that merged interval to merged. -3. Add all the intervals starting after newInterval to merged. +1. Add all the intervals ending before `newInterval` starts to `merged`. +2. Merge all overlapping intervals with `newInterval` and add that merged interval to `merged`. +3. Add all the intervals starting after `newInterval` to `merged`. ### Phase 1 -In this phase, we add all the intervals that end before newInterval starts to merged. This involves iterating through the -intervals list until the current interval no longer ends before newInterval starts (i.e. intervals[i][1] >= newInterval[0]). +In this phase, we add all the intervals that end before `newInterval` starts to `merged`. This involves iterating through the +`intervals` list until the current interval no longer ends before `newInterval` starts (i.e. `intervals[i][1] >= newInterval[0]`). ![Solution 1](./images/solutions/insert_interval_solution_1.png) ![Solution 2](./images/solutions/insert_interval_solution_2.png) @@ -43,9 +43,9 @@ intervals list until the current interval no longer ends before newInterval star ### Phase 2 In this phase, we merge all the intervals that overlap with newInterval together into a single interval by updating -newInterval to be the minimum start and maximum end of all the overlapping intervals. This involves iterating through -the intervals list until the current interval starts after newInterval ends (i.e. intervals[i][0] > newInterval[1]). -When that condition is met, we add newInterval to merged and move onto phase 3. +`newInterval` to be the minimum start and maximum end of all the overlapping intervals. This involves iterating through +the intervals list until the current interval starts after `newInterval` ends (i.e. `intervals[i][0] > newInterval[1]`). +When that condition is met, we add `newInterval` to merged and move onto phase 3. ![Solution 3](./images/solutions/insert_interval_solution_3.png) ![Solution 4](./images/solutions/insert_interval_solution_4.png) diff --git a/puzzles/arrays/array_3_pointers/README.md b/algorithms/two_pointers/array_3_pointers/README.md similarity index 100% rename from puzzles/arrays/array_3_pointers/README.md rename to algorithms/two_pointers/array_3_pointers/README.md diff --git a/puzzles/arrays/array_3_pointers/__init__.py b/algorithms/two_pointers/array_3_pointers/__init__.py similarity index 100% rename from puzzles/arrays/array_3_pointers/__init__.py rename to algorithms/two_pointers/array_3_pointers/__init__.py diff --git a/puzzles/arrays/array_3_pointers/test_array_3_pointers.py b/algorithms/two_pointers/array_3_pointers/test_array_3_pointers.py similarity index 100% rename from puzzles/arrays/array_3_pointers/test_array_3_pointers.py rename to algorithms/two_pointers/array_3_pointers/test_array_3_pointers.py diff --git a/algorithms/two_pointers/move_zeroes/README.md b/algorithms/two_pointers/move_zeroes/README.md new file mode 100644 index 00000000..07057a59 --- /dev/null +++ b/algorithms/two_pointers/move_zeroes/README.md @@ -0,0 +1,44 @@ +# Move Zeroes + +Given an integer array nums, move all 0's to the end of it while maintaining the relative order of the non-zero +elements. + +Note that you must do this in-place without making a copy of the array. + +```plain + +Example 1: + +Input: nums = [0,1,0,3,12] +Output: [1,3,12,0,0] +Example 2: + +Input: nums = [0] +Output: [0] +``` + +## Related Topics + +- Array +- Two Pointers + +## Solution + +We can solve this problem by keeping a pointer i that iterates through the array and another pointer nextNonZero that +points to the position where the next non-zero element should be placed. We can then swap the elements at i and nextNonZero +if the element at i is non-zero. This way, we can maintain the relative order of the non-zero elements while moving all +the zeroes to the end of the array. + +![Solution 1](./images/solutions/move_zeroes_solution_1.png) +![Solution 2](./images/solutions/move_zeroes_solution_2.png) +![Solution 3](./images/solutions/move_zeroes_solution_3.png) +![Solution 4](./images/solutions/move_zeroes_solution_4.png) +![Solution 5](./images/solutions/move_zeroes_solution_5.png) +![Solution 6](./images/solutions/move_zeroes_solution_6.png) +![Solution 7](./images/solutions/move_zeroes_solution_7.png) +![Solution 8](./images/solutions/move_zeroes_solution_8.png) +![Solution 9](./images/solutions/move_zeroes_solution_9.png) +![Solution 10](./images/solutions/move_zeroes_solution_10.png) +![Solution 11](./images/solutions/move_zeroes_solution_11.png) +![Solution 12](./images/solutions/move_zeroes_solution_12.png) +![Solution 13](./images/solutions/move_zeroes_solution_13.png) diff --git a/puzzles/arrays/move_zeroes/__init__.py b/algorithms/two_pointers/move_zeroes/__init__.py similarity index 90% rename from puzzles/arrays/move_zeroes/__init__.py rename to algorithms/two_pointers/move_zeroes/__init__.py index ca761d99..04c1a2a6 100644 --- a/puzzles/arrays/move_zeroes/__init__.py +++ b/algorithms/two_pointers/move_zeroes/__init__.py @@ -19,11 +19,12 @@ def move_zeroes(nums: List[int]) -> None: if len(nums) == 1: return - left_pointer = 0 - for current in range(len(nums)): - if nums[current] != 0: - nums[left_pointer], nums[current] = nums[current], nums[left_pointer] - left_pointer += 1 + next_non_zero = 0 + for idx in range(len(nums)): + if nums[idx] != 0: + if idx != next_non_zero: + nums[next_non_zero], nums[idx] = nums[idx], nums[next_non_zero] + next_non_zero += 1 def move_zeroes_one(nums: List[int]) -> None: diff --git a/algorithms/two_pointers/move_zeroes/images/solutions/move_zeroes_solution_1.png b/algorithms/two_pointers/move_zeroes/images/solutions/move_zeroes_solution_1.png new file mode 100644 index 00000000..e9d71820 Binary files /dev/null and b/algorithms/two_pointers/move_zeroes/images/solutions/move_zeroes_solution_1.png differ diff --git a/algorithms/two_pointers/move_zeroes/images/solutions/move_zeroes_solution_10.png b/algorithms/two_pointers/move_zeroes/images/solutions/move_zeroes_solution_10.png new file mode 100644 index 00000000..57aee7a5 Binary files /dev/null and b/algorithms/two_pointers/move_zeroes/images/solutions/move_zeroes_solution_10.png differ diff --git a/algorithms/two_pointers/move_zeroes/images/solutions/move_zeroes_solution_11.png b/algorithms/two_pointers/move_zeroes/images/solutions/move_zeroes_solution_11.png new file mode 100644 index 00000000..7d0881b8 Binary files /dev/null and b/algorithms/two_pointers/move_zeroes/images/solutions/move_zeroes_solution_11.png differ diff --git a/algorithms/two_pointers/move_zeroes/images/solutions/move_zeroes_solution_12.png b/algorithms/two_pointers/move_zeroes/images/solutions/move_zeroes_solution_12.png new file mode 100644 index 00000000..002a4cb5 Binary files /dev/null and b/algorithms/two_pointers/move_zeroes/images/solutions/move_zeroes_solution_12.png differ diff --git a/algorithms/two_pointers/move_zeroes/images/solutions/move_zeroes_solution_13.png b/algorithms/two_pointers/move_zeroes/images/solutions/move_zeroes_solution_13.png new file mode 100644 index 00000000..49ac040c Binary files /dev/null and b/algorithms/two_pointers/move_zeroes/images/solutions/move_zeroes_solution_13.png differ diff --git a/algorithms/two_pointers/move_zeroes/images/solutions/move_zeroes_solution_2.png b/algorithms/two_pointers/move_zeroes/images/solutions/move_zeroes_solution_2.png new file mode 100644 index 00000000..704457a5 Binary files /dev/null and b/algorithms/two_pointers/move_zeroes/images/solutions/move_zeroes_solution_2.png differ diff --git a/algorithms/two_pointers/move_zeroes/images/solutions/move_zeroes_solution_3.png b/algorithms/two_pointers/move_zeroes/images/solutions/move_zeroes_solution_3.png new file mode 100644 index 00000000..06f7c216 Binary files /dev/null and b/algorithms/two_pointers/move_zeroes/images/solutions/move_zeroes_solution_3.png differ diff --git a/algorithms/two_pointers/move_zeroes/images/solutions/move_zeroes_solution_4.png b/algorithms/two_pointers/move_zeroes/images/solutions/move_zeroes_solution_4.png new file mode 100644 index 00000000..74128457 Binary files /dev/null and b/algorithms/two_pointers/move_zeroes/images/solutions/move_zeroes_solution_4.png differ diff --git a/algorithms/two_pointers/move_zeroes/images/solutions/move_zeroes_solution_5.png b/algorithms/two_pointers/move_zeroes/images/solutions/move_zeroes_solution_5.png new file mode 100644 index 00000000..d8964b24 Binary files /dev/null and b/algorithms/two_pointers/move_zeroes/images/solutions/move_zeroes_solution_5.png differ diff --git a/algorithms/two_pointers/move_zeroes/images/solutions/move_zeroes_solution_6.png b/algorithms/two_pointers/move_zeroes/images/solutions/move_zeroes_solution_6.png new file mode 100644 index 00000000..0b0c8f48 Binary files /dev/null and b/algorithms/two_pointers/move_zeroes/images/solutions/move_zeroes_solution_6.png differ diff --git a/algorithms/two_pointers/move_zeroes/images/solutions/move_zeroes_solution_7.png b/algorithms/two_pointers/move_zeroes/images/solutions/move_zeroes_solution_7.png new file mode 100644 index 00000000..9811d747 Binary files /dev/null and b/algorithms/two_pointers/move_zeroes/images/solutions/move_zeroes_solution_7.png differ diff --git a/algorithms/two_pointers/move_zeroes/images/solutions/move_zeroes_solution_8.png b/algorithms/two_pointers/move_zeroes/images/solutions/move_zeroes_solution_8.png new file mode 100644 index 00000000..3270beeb Binary files /dev/null and b/algorithms/two_pointers/move_zeroes/images/solutions/move_zeroes_solution_8.png differ diff --git a/algorithms/two_pointers/move_zeroes/images/solutions/move_zeroes_solution_9.png b/algorithms/two_pointers/move_zeroes/images/solutions/move_zeroes_solution_9.png new file mode 100644 index 00000000..46d246cb Binary files /dev/null and b/algorithms/two_pointers/move_zeroes/images/solutions/move_zeroes_solution_9.png differ diff --git a/algorithms/two_pointers/move_zeroes/test_move_zeroes.py b/algorithms/two_pointers/move_zeroes/test_move_zeroes.py new file mode 100644 index 00000000..c77af71d --- /dev/null +++ b/algorithms/two_pointers/move_zeroes/test_move_zeroes.py @@ -0,0 +1,31 @@ +import unittest +from typing import List +from parameterized import parameterized +from algorithms.two_pointers.move_zeroes import move_zeroes, move_zeroes_one + +MOVE_ZEROES_TEST_CASES = [ + ([0, 1, 0, 3, 12], [1, 3, 12, 0, 0]), + ([0], [0]), + ([0, 0, 0], [0, 0, 0]), + ([1, 0], [1, 0]), + ([2, 0, 4, 0, 9], [2, 4, 9, 0, 0]), + ([1, 0, 4, 0, 3, 0, 1], [1, 4, 3, 1, 0, 0, 0]), + ([0, 0, 1], [1, 0, 0]), + ([1, 2, 3], [1, 2, 3]), +] + + +class MoveZeroesTestCase(unittest.TestCase): + @parameterized.expand(MOVE_ZEROES_TEST_CASES) + def test_move_zeroes(self, nums: List[int], expected: List[int]): + move_zeroes(nums) + self.assertEqual(expected, nums) + + @parameterized.expand(MOVE_ZEROES_TEST_CASES) + def test_move_zeroes_with_intermediate(self, nums: List[int], expected: List[int]): + move_zeroes_one(nums) + self.assertEqual(expected, nums) + + +if __name__ == "__main__": + unittest.main() diff --git a/algorithms/two_pointers/rain_water_trapped/README.md b/algorithms/two_pointers/rain_water_trapped/README.md new file mode 100644 index 00000000..953e7706 --- /dev/null +++ b/algorithms/two_pointers/rain_water_trapped/README.md @@ -0,0 +1,107 @@ +# Trapping Rain-Water + +Given an integer array A of non-negative integers representing an elevation map where the width of each bar is 1, +compute how much water it is able to trap after raining. + +Input Format +The only argument given is integer array A. + +Output Format +Return the total water it is able to trap after raining. + +```plain +Example Input +Input 1: + +A = [0, 1, 0, 2, 1, 0, 1, 3, 2, 1, 2, 1] +Input 2: + +A = [1, 2] + +Example Output +Output 1: + +6 +Output 2: + +0 + +Example Explanation +Explanation 1: + +In this case, 6 units of rain water (blue section) are being trapped. +Explanation 2: + +No water is trapped. +``` + +## Related Topics + +- Array +- Two Pointers +- Dynamic Programming +- Stack +- Monotonic Stack + +## Solution + +We can use the two-pointer technique to solve this problem in O(n) time and O(1) space. +In order for any index in the array to be able to trap rain water, there must be higher bars on both the left and right +side of the index. For example, index 2 in the following array has height 1. It can trap water because there are higher +bars to the left and right of it. + +![Solution 1](./images/solutions/trapped_rain_water_solution_1.png) + +To calculate the exact amount of water that can be trapped at index 2, we first take the minimum height of the highest +bars to the left and right of it, which in this case is 4. We then subtract the height of the bar at index 2, which is 1, + +![Solution 2](./images/solutions/trapped_rain_water_solution_2.png) + +So if we knew the height of the highest bars to the left and right of every index, we could iterate through the array +and calculate the amount of water that can be trapped at each index. +But we don't need to know the exact height of both the highest bars to the left and right of every index. For example, +let's say we know the highest bar to the right of index 7 with height 0 has a height of 2. + +![Solution 3](./images/solutions/trapped_rain_water_solution_3.png) + +If we also knew that there exists a higher bar than 2 anywhere to the left of index 7, then we also know that the minimum +height of the highest bars to the left and right of index 7 is 2. This means that we have enough information to calculate +the amount of water that can be trapped at index 7, which is 2 - 0 = 2. +This is the insight behind how the two-pointer technique can be used to solve this problem. We initialize two pointers +left and right at opposite ends of the array. We also keep two variables leftMax and rightMax to keep track of the highest +bars each pointer has seen. + +![Solution 4](./images/solutions/trapped_rain_water_solution_4.png) + +We now use the values of leftMax and rightMax to visit every single index in the array exactly once. We start by +comparing leftMax and rightMax. In this case, rightMax is smaller than leftMax, so we know that: + +1. The maximum height of the highest bar to the right of right - 1 is rightMax +2. There exists a higher bar than rightMax somewhere to the left of right + +These two facts mean that we have enough information to calculate the amount of water that can be trapped at index +right - 1. So first we move the right pointer back by 1: + +![Solution 5](./images/solutions/trapped_rain_water_solution_5.png) + +There are two possible cases to consider when calculating the amount of water that can be trapped at the current index +of right: +1. The height of the bar at index right is smaller than rightMax +2. The height of the bar at index right is greater than or equal to rightMax + +In our case, the height of the bar at index right is smaller than rightMax, so we know that the amount of water that +can be trapped at index 1 is rightMax - height[right], and we can move to the next iteration, which follows the same logic: + +![Solution 6](./images/solutions/trapped_rain_water_solution_6.png) +![Solution 7](./images/solutions/trapped_rain_water_solution_7.png) +![Solution 8](./images/solutions/trapped_rain_water_solution_8.png) +![Solution 9](./images/solutions/trapped_rain_water_solution_9.png) + +Now, we run into case 2, where height[right] is greater than or equal to rightMax. This means we can't trap any water at +this index, so instead we update rightMax to the height of the bar at index right to prepare for the next iteration. + +![Solution 10](./images/solutions/trapped_rain_water_solution_10.png) +![Solution 11](./images/solutions/trapped_rain_water_solution_11.png) + +The same logic applies when leftMax is less than rightMax, and this continues until every index has been visited exactly +once, for a total time complexity of O(n) and a space complexity of O(1). diff --git a/algorithms/two_pointers/rain_water_trapped/__init__.py b/algorithms/two_pointers/rain_water_trapped/__init__.py new file mode 100644 index 00000000..aa4f4381 --- /dev/null +++ b/algorithms/two_pointers/rain_water_trapped/__init__.py @@ -0,0 +1,27 @@ +from typing import List + + +def trapped_rain_water(heights: List[int]) -> int: + if not heights: + return 0 + # Initialize the pointers left and right at both ends of the array + left, right = 0, len(heights) - 1 + # initialize left_max and right_max that will keep track of the highest bars each pointer has seen + left_max, right_max = heights[left], heights[right] + # Keeps track of the total trapped rain wayter + result = 0 + + while left < right: + if left_max < right_max: + left += 1 + if heights[left] >= left_max: + left_max = heights[left] + else: + result += left_max - heights[left] + else: + right -= 1 + if heights[right] >= right_max: + right_max = heights[right] + else: + result += right_max - heights[right] + return result diff --git a/algorithms/two_pointers/rain_water_trapped/images/solutions/trapped_rain_water_solution_1.png b/algorithms/two_pointers/rain_water_trapped/images/solutions/trapped_rain_water_solution_1.png new file mode 100644 index 00000000..485086e9 Binary files /dev/null and b/algorithms/two_pointers/rain_water_trapped/images/solutions/trapped_rain_water_solution_1.png differ diff --git a/algorithms/two_pointers/rain_water_trapped/images/solutions/trapped_rain_water_solution_10.png b/algorithms/two_pointers/rain_water_trapped/images/solutions/trapped_rain_water_solution_10.png new file mode 100644 index 00000000..c1b5cd53 Binary files /dev/null and b/algorithms/two_pointers/rain_water_trapped/images/solutions/trapped_rain_water_solution_10.png differ diff --git a/algorithms/two_pointers/rain_water_trapped/images/solutions/trapped_rain_water_solution_11.png b/algorithms/two_pointers/rain_water_trapped/images/solutions/trapped_rain_water_solution_11.png new file mode 100644 index 00000000..237a36df Binary files /dev/null and b/algorithms/two_pointers/rain_water_trapped/images/solutions/trapped_rain_water_solution_11.png differ diff --git a/algorithms/two_pointers/rain_water_trapped/images/solutions/trapped_rain_water_solution_2.png b/algorithms/two_pointers/rain_water_trapped/images/solutions/trapped_rain_water_solution_2.png new file mode 100644 index 00000000..af48aef7 Binary files /dev/null and b/algorithms/two_pointers/rain_water_trapped/images/solutions/trapped_rain_water_solution_2.png differ diff --git a/algorithms/two_pointers/rain_water_trapped/images/solutions/trapped_rain_water_solution_3.png b/algorithms/two_pointers/rain_water_trapped/images/solutions/trapped_rain_water_solution_3.png new file mode 100644 index 00000000..95d3b82f Binary files /dev/null and b/algorithms/two_pointers/rain_water_trapped/images/solutions/trapped_rain_water_solution_3.png differ diff --git a/algorithms/two_pointers/rain_water_trapped/images/solutions/trapped_rain_water_solution_4.png b/algorithms/two_pointers/rain_water_trapped/images/solutions/trapped_rain_water_solution_4.png new file mode 100644 index 00000000..5fa42669 Binary files /dev/null and b/algorithms/two_pointers/rain_water_trapped/images/solutions/trapped_rain_water_solution_4.png differ diff --git a/algorithms/two_pointers/rain_water_trapped/images/solutions/trapped_rain_water_solution_5.png b/algorithms/two_pointers/rain_water_trapped/images/solutions/trapped_rain_water_solution_5.png new file mode 100644 index 00000000..1d7f0f3d Binary files /dev/null and b/algorithms/two_pointers/rain_water_trapped/images/solutions/trapped_rain_water_solution_5.png differ diff --git a/algorithms/two_pointers/rain_water_trapped/images/solutions/trapped_rain_water_solution_6.png b/algorithms/two_pointers/rain_water_trapped/images/solutions/trapped_rain_water_solution_6.png new file mode 100644 index 00000000..82a12b4c Binary files /dev/null and b/algorithms/two_pointers/rain_water_trapped/images/solutions/trapped_rain_water_solution_6.png differ diff --git a/algorithms/two_pointers/rain_water_trapped/images/solutions/trapped_rain_water_solution_7.png b/algorithms/two_pointers/rain_water_trapped/images/solutions/trapped_rain_water_solution_7.png new file mode 100644 index 00000000..53c206d6 Binary files /dev/null and b/algorithms/two_pointers/rain_water_trapped/images/solutions/trapped_rain_water_solution_7.png differ diff --git a/algorithms/two_pointers/rain_water_trapped/images/solutions/trapped_rain_water_solution_8.png b/algorithms/two_pointers/rain_water_trapped/images/solutions/trapped_rain_water_solution_8.png new file mode 100644 index 00000000..92de6154 Binary files /dev/null and b/algorithms/two_pointers/rain_water_trapped/images/solutions/trapped_rain_water_solution_8.png differ diff --git a/algorithms/two_pointers/rain_water_trapped/images/solutions/trapped_rain_water_solution_9.png b/algorithms/two_pointers/rain_water_trapped/images/solutions/trapped_rain_water_solution_9.png new file mode 100644 index 00000000..53a4eeaa Binary files /dev/null and b/algorithms/two_pointers/rain_water_trapped/images/solutions/trapped_rain_water_solution_9.png differ diff --git a/algorithms/two_pointers/rain_water_trapped/test_trapped_rain_water.py b/algorithms/two_pointers/rain_water_trapped/test_trapped_rain_water.py new file mode 100644 index 00000000..b769c787 --- /dev/null +++ b/algorithms/two_pointers/rain_water_trapped/test_trapped_rain_water.py @@ -0,0 +1,22 @@ +import unittest +from typing import List +from parameterized import parameterized +from algorithms.two_pointers.rain_water_trapped import trapped_rain_water + +TRAPPING_RAIN_WATER = [ + ([0, 1, 0, 2, 1, 0, 1, 3, 2, 1, 2, 1], 6), + ([1, 2], 0), + ([4, 2, 0, 3, 2, 5], 9), + ([3, 4, 1, 2, 2, 5, 1, 0, 2], 10), +] + + +class TrappedRainWaterTestCase(unittest.TestCase): + @parameterized.expand(TRAPPING_RAIN_WATER) + def test_trapping_rain_water(self, heights: List[int], expected: int): + actual = trapped_rain_water(heights) + self.assertEqual(expected, actual) + + +if __name__ == "__main__": + unittest.main() diff --git a/algorithms/two_pointers/sort_colors/README.md b/algorithms/two_pointers/sort_colors/README.md index e46cae49..bf8b5183 100644 --- a/algorithms/two_pointers/sort_colors/README.md +++ b/algorithms/two_pointers/sort_colors/README.md @@ -16,3 +16,60 @@ Input: colors = [2,0,2,1,1,0] Output: [0,0,1,1,2,2] ``` +## Solution + +- [Sorting 0s](#sorting-os) +- [Sorting 1s](#sorting-1s) +- [Sorting 2s](#sorting-2s) +- [Termination](#termination) + +We can understand this algorithm by looking at the invariants which hold true after each iteration: + +1. All elements to the left of the left are 0s. +2. All elements between left and i - 1 are 1s. +3. All elements between i and right are unsorted. +4. All elements to the right of right are 2s. + +![Solution 1](./images/solutions/sort_colors_solution_1.png) +![Solution 2](./images/solutions/sort_colors_solution_2.png) + +Let's now see how we maintain these invariants as we iterate through the array. + +### Sorting Os + +When nums[i] is equal to 0, invariant 2 tells us there are two possible values for left: 0 or 1. +Let's consider the case when left == 1 first. We swap i with left. This allows us to increment the left pointer to +maintain invariant 1. Since we know that the new item at i is a 1, we can also increment i to maintain variant 2. + +![Solution 3](./images/solutions/sort_colors_solution_3.png) +![Solution 4](./images/solutions/sort_colors_solution_4.png) + +Now let's consider what happens when left == 0, which happens when we haven't encountered any 1s yet and i == left. We +swap i with left (which is itself) and increment left to maintain invariant 1. Since we still haven't encountered any 1s, +we can increment i to maintain invariant 2. In other words, the "ones" region remains empty. + +![Solution 5](./images/solutions/sort_colors_solution_5.png) +![Solution 6](./images/solutions/sort_colors_solution_6.png) + +### Sorting 1s + +When nums[i] == 1, we can simply increment i to maintain invariant 2. + +![Solution 7](./images/solutions/sort_colors_solution_7.png) +![Solution 8](./images/solutions/sort_colors_solution_8.png) + +### Sorting 2s + +When nums[i] == 2, we swap nums[i] with nums[right]. This allows us to decrement right to maintain invariant 4. But +since the new item at i came from the unsorted region, the new item at i is still unsorted, so we have to go through +another iteration to correctly sort it. + +![Solution 9](./images/solutions/sort_colors_solution_9.png) +![Solution 10](./images/solutions/sort_colors_solution_10.png) + +### Termination + +When i surpasses right the unsorted region is empty and the entire array is sorted. + +![Solution 11](./images/solutions/sort_colors_solution_11.png) +![Solution 12](./images/solutions/sort_colors_solution_12.png) diff --git a/algorithms/two_pointers/sort_colors/__init__.py b/algorithms/two_pointers/sort_colors/__init__.py index ddc101ac..dbcb8c7a 100644 --- a/algorithms/two_pointers/sort_colors/__init__.py +++ b/algorithms/two_pointers/sort_colors/__init__.py @@ -22,51 +22,25 @@ def sort_colors(colors: List[int]) -> List[int]: Returns: list: sorted list of 0s, 1s, and 2s """ + # Initialize the three pointers, low to track 0s, mid to track 1s and high to track 2s low, mid, high = 0, 0, len(colors) - 1 + # Perform our loop. + # We compare mid and high pointers because the elements at low will be swapped with elements at mid if necessary. + # And mid will be moving up until the end of the list, while high will be moving down until it is equal to high. while mid <= high: if colors[mid] == 0: + # Swap the mid and the low elements and move the low pointer up and the mid pointer up to find the next + # elements that are potentially 0 and 1 colors[low], colors[mid] = colors[mid], colors[low] low += 1 mid += 1 elif colors[mid] == 1: + # Move the 'mid' pointer up, because the element is already at the correct position, no need to swap it mid += 1 else: + # Move the high pointer down. We don't move the mid pointer, because it could be a 0 or a 1 colors[mid], colors[high] = colors[high], colors[mid] high -= 1 return colors - - -def sort_colors_v2(colors: List[int]) -> List[int]: - """ - Sorts a list of 0s, 1s, and 2s in place using two pointers - - This algorithm uses two pointers: low and high. The low pointer is used to track the position where the next 0 - should be placed, and the high pointer is used to track the position where the next 2 should be placed. - - The algorithm works by iterating through the list with the low pointer. If it encounters a 0, it simply increments - low. If it encounters a 2, it decrements high. If it encounters a 1, it swaps it with the element at the high index - and decrements high. This swapping process ensures that all the 1s are placed between the 0s and the 2s. - - This algorithm has a time complexity of O(n) and a space complexity of O(1), making it efficient for sorting lists - of 0s, 1s, and 2s using only two pointers. - - Args: - colors (list): list of 0s, 1s, and 2s - Returns: - list: sorted list of 0s, 1s, and 2s - """ - red, blue = 0, len(colors) - 1 - i = 0 - while i <= blue: - if colors[i] == 0: - colors[red], colors[i] = colors[i], colors[red] - red += 1 - i += 1 - elif colors[i] == 2: - colors[blue], colors[i] = colors[i], colors[blue] - blue -= 1 - else: - i += 1 - return colors diff --git a/algorithms/two_pointers/sort_colors/images/solutions/sort_colors_solution_1.png b/algorithms/two_pointers/sort_colors/images/solutions/sort_colors_solution_1.png new file mode 100644 index 00000000..12b6e505 Binary files /dev/null and b/algorithms/two_pointers/sort_colors/images/solutions/sort_colors_solution_1.png differ diff --git a/algorithms/two_pointers/sort_colors/images/solutions/sort_colors_solution_10.png b/algorithms/two_pointers/sort_colors/images/solutions/sort_colors_solution_10.png new file mode 100644 index 00000000..5153dd0c Binary files /dev/null and b/algorithms/two_pointers/sort_colors/images/solutions/sort_colors_solution_10.png differ diff --git a/algorithms/two_pointers/sort_colors/images/solutions/sort_colors_solution_11.png b/algorithms/two_pointers/sort_colors/images/solutions/sort_colors_solution_11.png new file mode 100644 index 00000000..c8119bf2 Binary files /dev/null and b/algorithms/two_pointers/sort_colors/images/solutions/sort_colors_solution_11.png differ diff --git a/algorithms/two_pointers/sort_colors/images/solutions/sort_colors_solution_12.png b/algorithms/two_pointers/sort_colors/images/solutions/sort_colors_solution_12.png new file mode 100644 index 00000000..c77e1974 Binary files /dev/null and b/algorithms/two_pointers/sort_colors/images/solutions/sort_colors_solution_12.png differ diff --git a/algorithms/two_pointers/sort_colors/images/solutions/sort_colors_solution_2.png b/algorithms/two_pointers/sort_colors/images/solutions/sort_colors_solution_2.png new file mode 100644 index 00000000..6b50fba6 Binary files /dev/null and b/algorithms/two_pointers/sort_colors/images/solutions/sort_colors_solution_2.png differ diff --git a/algorithms/two_pointers/sort_colors/images/solutions/sort_colors_solution_3.png b/algorithms/two_pointers/sort_colors/images/solutions/sort_colors_solution_3.png new file mode 100644 index 00000000..3ff683bb Binary files /dev/null and b/algorithms/two_pointers/sort_colors/images/solutions/sort_colors_solution_3.png differ diff --git a/algorithms/two_pointers/sort_colors/images/solutions/sort_colors_solution_4.png b/algorithms/two_pointers/sort_colors/images/solutions/sort_colors_solution_4.png new file mode 100644 index 00000000..452cacdb Binary files /dev/null and b/algorithms/two_pointers/sort_colors/images/solutions/sort_colors_solution_4.png differ diff --git a/algorithms/two_pointers/sort_colors/images/solutions/sort_colors_solution_5.png b/algorithms/two_pointers/sort_colors/images/solutions/sort_colors_solution_5.png new file mode 100644 index 00000000..9c91c26e Binary files /dev/null and b/algorithms/two_pointers/sort_colors/images/solutions/sort_colors_solution_5.png differ diff --git a/algorithms/two_pointers/sort_colors/images/solutions/sort_colors_solution_6.png b/algorithms/two_pointers/sort_colors/images/solutions/sort_colors_solution_6.png new file mode 100644 index 00000000..65cc4747 Binary files /dev/null and b/algorithms/two_pointers/sort_colors/images/solutions/sort_colors_solution_6.png differ diff --git a/algorithms/two_pointers/sort_colors/images/solutions/sort_colors_solution_7.png b/algorithms/two_pointers/sort_colors/images/solutions/sort_colors_solution_7.png new file mode 100644 index 00000000..eea245ab Binary files /dev/null and b/algorithms/two_pointers/sort_colors/images/solutions/sort_colors_solution_7.png differ diff --git a/algorithms/two_pointers/sort_colors/images/solutions/sort_colors_solution_8.png b/algorithms/two_pointers/sort_colors/images/solutions/sort_colors_solution_8.png new file mode 100644 index 00000000..968c20c2 Binary files /dev/null and b/algorithms/two_pointers/sort_colors/images/solutions/sort_colors_solution_8.png differ diff --git a/algorithms/two_pointers/sort_colors/images/solutions/sort_colors_solution_9.png b/algorithms/two_pointers/sort_colors/images/solutions/sort_colors_solution_9.png new file mode 100644 index 00000000..91aaf054 Binary files /dev/null and b/algorithms/two_pointers/sort_colors/images/solutions/sort_colors_solution_9.png differ diff --git a/algorithms/two_pointers/sort_colors/test_sort_colors.py b/algorithms/two_pointers/sort_colors/test_sort_colors.py index ff008baa..bb101646 100644 --- a/algorithms/two_pointers/sort_colors/test_sort_colors.py +++ b/algorithms/two_pointers/sort_colors/test_sort_colors.py @@ -1,119 +1,27 @@ import unittest -from . import sort_colors, sort_colors_v2 - - -class SortColorsV2TestCase(unittest.TestCase): - def test_1(self): - """should sort colors = [1,0,2,1,2,2] to [0,1,1,2,2,2]""" - colors = [1, 0, 2, 1, 2, 2] - expected = [0, 1, 1, 2, 2, 2] - actual = sort_colors_v2(colors) - self.assertEqual(expected, actual) - - def test_2(self): - """should sort colors = [0,1,1,2,0,2,0,2,1,2] to [0,0,0,1,1,1,2,2,2,2]""" - colors = [0, 1, 1, 2, 0, 2, 0, 2, 1, 2] - expected = [0, 0, 0, 1, 1, 1, 2, 2, 2, 2] - actual = sort_colors_v2(colors) - self.assertEqual(expected, actual) - - def test_3(self): - """should sort colors = [0] to [0]""" - colors = [0] - expected = [0] - actual = sort_colors_v2(colors) - self.assertEqual(expected, actual) - - def test_4(self): - """should sort colors = [0,1,0] to [0,0,1]""" - colors = [0, 1, 0] - expected = [0, 0, 1] - actual = sort_colors_v2(colors) - self.assertEqual(expected, actual) - - def test_5(self): - """should sort colors = [1] to [1]""" - colors = [1] - expected = [1] - actual = sort_colors_v2(colors) - self.assertEqual(expected, actual) - - def test_6(self): - """should sort colors = [2,2] to [2,2]""" - colors = [2, 2] - expected = [2, 2] - actual = sort_colors_v2(colors) - self.assertEqual(expected, actual) - - def test_7(self): - """should sort colors = [1,1,0,2] to [0,1,1,2]""" - colors = [1, 1, 0, 2] - expected = [0, 1, 1, 2] - actual = sort_colors_v2(colors) - self.assertEqual(expected, actual) - - def test_8(self): - """should sort colors = [2,1,1,0,0] to [0,0,1,1,2]""" - colors = [2, 1, 1, 0, 0] - expected = [0, 0, 1, 1, 2] - actual = sort_colors_v2(colors) - self.assertEqual(expected, actual) +from typing import List +from parameterized import parameterized +from algorithms.two_pointers.sort_colors import sort_colors + +SORT_COLORS_TEST_CASES = [ + ([1, 0, 2, 1, 2, 2], [0, 1, 1, 2, 2, 2]), + ([0, 1, 1, 2, 0, 2, 0, 2, 1, 2], [0, 0, 0, 1, 1, 1, 2, 2, 2, 2]), + ([0], [0]), + ([0, 1, 0], [0, 0, 1]), + ([1], [1]), + ([2, 2], [2, 2]), + ([1, 2, 0], [0, 1, 2]), + ([2, 2, 1], [1, 2, 2]), + ([2, 1, 2, 0, 1, 2, 2, 0], [0, 0, 1, 1, 2, 2, 2, 2]), + ([1, 1, 0, 2], [0, 1, 1, 2]), + ([2, 1, 1, 0, 0], [0, 0, 1, 1, 2]), + ([2, 0, 2, 1, 1, 0], [0, 0, 1, 1, 2, 2]), +] class SortColorsTestCase(unittest.TestCase): - def test_1(self): - """should sort colors = [1,0,2,1,2,2] to [0,1,1,2,2,2]""" - colors = [1, 0, 2, 1, 2, 2] - expected = [0, 1, 1, 2, 2, 2] - actual = sort_colors(colors) - self.assertEqual(expected, actual) - - def test_2(self): - """should sort colors = [0,1,1,2,0,2,0,2,1,2] to [0,0,0,1,1,1,2,2,2,2]""" - colors = [0, 1, 1, 2, 0, 2, 0, 2, 1, 2] - expected = [0, 0, 0, 1, 1, 1, 2, 2, 2, 2] - actual = sort_colors(colors) - self.assertEqual(expected, actual) - - def test_3(self): - """should sort colors = [0] to [0]""" - colors = [0] - expected = [0] - actual = sort_colors(colors) - self.assertEqual(expected, actual) - - def test_4(self): - """should sort colors = [0,1,0] to [0,0,1]""" - colors = [0, 1, 0] - expected = [0, 0, 1] - actual = sort_colors(colors) - self.assertEqual(expected, actual) - - def test_5(self): - """should sort colors = [1] to [1]""" - colors = [1] - expected = [1] - actual = sort_colors(colors) - self.assertEqual(expected, actual) - - def test_6(self): - """should sort colors = [2,2] to [2,2]""" - colors = [2, 2] - expected = [2, 2] - actual = sort_colors(colors) - self.assertEqual(expected, actual) - - def test_7(self): - """should sort colors = [1,1,0,2] to [0,1,1,2]""" - colors = [1, 1, 0, 2] - expected = [0, 1, 1, 2] - actual = sort_colors(colors) - self.assertEqual(expected, actual) - - def test_8(self): - """should sort colors = [2,1,1,0,0] to [0,0,1,1,2]""" - colors = [2, 1, 1, 0, 0] - expected = [0, 0, 1, 1, 2] + @parameterized.expand(SORT_COLORS_TEST_CASES) + def test_sort_colors(self, colors: List[int], expected: List[int]): actual = sort_colors(colors) self.assertEqual(expected, actual) diff --git a/algorithms/two_pointers/three_sum/README.md b/algorithms/two_pointers/three_sum/README.md index a8a8759e..50c28079 100644 --- a/algorithms/two_pointers/three_sum/README.md +++ b/algorithms/two_pointers/three_sum/README.md @@ -32,3 +32,61 @@ Input: nums = [0,0,0] Output: [[0,0,0]] Explanation: The only possible triplet sums up to 0. ``` + +## Solution + +We can leverage the two-pointer technique to solve this problem by first sorting the array. We can then iterate through +each element in the array. The problem then reduces to finding two numbers in the rest of the array that sum to the +negative of the current element, which follows the same logic as the Two Sum (Sorted Array) problem. + +![Solution 1](./images/solutions/three_sum_solution_1.png) + +Since our first triplet sums to 0, we can add it to our result set. + +![Solution 2](./images/solutions/three_sum_solution_2.png) +![Solution 3](./images/solutions/three_sum_solution_3.png) +![Solution 4](./images/solutions/three_sum_solution_4.png) +![Solution 5](./images/solutions/three_sum_solution_5.png) +![Solution 6](./images/solutions/three_sum_solution_6.png) + +### Avoiding Duplicates + +As soon as we find a triplet that sums to 0, we can add it to our result set. We then have to move our left and right +pointers to look for the next triplet while avoiding duplicate triplets. We can do this by moving the left and right +pointers until they point to different numbers than the ones they were pointing to before. +Here we move the left pointer once until it reaches the last -1 in the array. Then, we can move both the left and right +pointers so that they both point to new numbers. + +![Solution 7](./images/solutions/three_sum_solution_7.png) +![Solution 8](./images/solutions/three_sum_solution_8.png) + +Here we can do another iteration of the Two Sum problem using the new positions of the left and right pointers. + +![Solution 9](./images/solutions/three_sum_solution_9.png) +![Solution 10](./images/solutions/three_sum_solution_10.png) +![Solution 11](./images/solutions/three_sum_solution_11.png) + +At this point our left and right pointers have crossed, so we can move our iterator to the next number in the array. + +### Avoiding Duplicates II + +In this case, since the next number in the array is the same as the previous number, we can skip it. We can do this by +moving our iterator until it points to a new number. + +![Solution 12](./images/solutions/three_sum_solution_12.png) +![Solution 13](./images/solutions/three_sum_solution_13.png) +![Solution 14](./images/solutions/three_sum_solution_14.png) + +And we're ready to start the Two Sum algorithm again, so we reset our left and right pointers, and start the algorithm. + +![Solution 15](./images/solutions/three_sum_solution_15.png) +![Solution 16](./images/solutions/three_sum_solution_16.png) +![Solution 17](./images/solutions/three_sum_solution_17.png) + +### Termination + +Our algorithm terminates when i reaches the 3rd to last element in the array (i.e., i < n - 2). This is because we need +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) diff --git a/algorithms/two_pointers/three_sum/__init__.py b/algorithms/two_pointers/three_sum/__init__.py index a0978d97..012cc0fc 100644 --- a/algorithms/two_pointers/three_sum/__init__.py +++ b/algorithms/two_pointers/three_sum/__init__.py @@ -4,9 +4,14 @@ def three_sum(nums: List[int]) -> List[List[int]]: """ Complexity Analysis: + We assume that n is the length of the input array - Time Complexity: O(nlog(n)) + O(n^2) = O(n^2) the O(nlog(n)) is due to sorting - Space Complexity: O(1) as no extra space is taken up + Time Complexity: O(nlog(n)) + O(n^2) = O(n^2) the O(nlog(n)) is due to sorting, overall, the time complexity is O(n²). + This is due to the nested loops in the algorithm. We perform n iterations of the outer loop, and each iteration + takes O(n) time to use the two-pointer technique. + + Space Complexity: O(n²) as no extra space is taken up. We need to store all distinct triplets that sum to 0, which + can be at most O(n²) triplets. Args: nums (list): input list of integers @@ -14,24 +19,33 @@ def three_sum(nums: List[int]) -> List[List[int]]: list: list of lists of integers """ result = [] - # Time Complexity: O(nlog(n)) + # Time Complexity: O(nlog(n)) sorting in place. This may incur space complexity of O(n) due to Python's timesort + # using temporary storage to handle the in place sorting nums.sort() for idx, num in enumerate(nums): + # Increment to avoid duplicates if idx > 0 and num == nums[idx - 1]: continue left, right = idx + 1, len(nums) - 1 while left < right: - sum_ = num + nums[left] + nums[right] - if sum_ > 0: + total = num + nums[left] + nums[right] + if total > 0: right -= 1 - elif sum_ < 0: + elif total < 0: left += 1 else: + # add the triplet result.append([num, nums[left], nums[right]]) - left += 1 - while nums[left] == nums[left - 1] and left < 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 + return result diff --git a/algorithms/two_pointers/three_sum/images/solutions/three_sum_solution_1.png b/algorithms/two_pointers/three_sum/images/solutions/three_sum_solution_1.png new file mode 100644 index 00000000..15ce0736 Binary files /dev/null and b/algorithms/two_pointers/three_sum/images/solutions/three_sum_solution_1.png differ diff --git a/algorithms/two_pointers/three_sum/images/solutions/three_sum_solution_10.png b/algorithms/two_pointers/three_sum/images/solutions/three_sum_solution_10.png new file mode 100644 index 00000000..daea4ada Binary files /dev/null and b/algorithms/two_pointers/three_sum/images/solutions/three_sum_solution_10.png differ diff --git a/algorithms/two_pointers/three_sum/images/solutions/three_sum_solution_11.png b/algorithms/two_pointers/three_sum/images/solutions/three_sum_solution_11.png new file mode 100644 index 00000000..101d97a7 Binary files /dev/null and b/algorithms/two_pointers/three_sum/images/solutions/three_sum_solution_11.png differ diff --git a/algorithms/two_pointers/three_sum/images/solutions/three_sum_solution_12.png b/algorithms/two_pointers/three_sum/images/solutions/three_sum_solution_12.png new file mode 100644 index 00000000..33f2a8f0 Binary files /dev/null and b/algorithms/two_pointers/three_sum/images/solutions/three_sum_solution_12.png differ diff --git a/algorithms/two_pointers/three_sum/images/solutions/three_sum_solution_13.png b/algorithms/two_pointers/three_sum/images/solutions/three_sum_solution_13.png new file mode 100644 index 00000000..506ccb96 Binary files /dev/null and b/algorithms/two_pointers/three_sum/images/solutions/three_sum_solution_13.png differ diff --git a/algorithms/two_pointers/three_sum/images/solutions/three_sum_solution_14.png b/algorithms/two_pointers/three_sum/images/solutions/three_sum_solution_14.png new file mode 100644 index 00000000..3e962bac Binary files /dev/null and b/algorithms/two_pointers/three_sum/images/solutions/three_sum_solution_14.png differ diff --git a/algorithms/two_pointers/three_sum/images/solutions/three_sum_solution_15.png b/algorithms/two_pointers/three_sum/images/solutions/three_sum_solution_15.png new file mode 100644 index 00000000..e23d0597 Binary files /dev/null and b/algorithms/two_pointers/three_sum/images/solutions/three_sum_solution_15.png differ diff --git a/algorithms/two_pointers/three_sum/images/solutions/three_sum_solution_16.png b/algorithms/two_pointers/three_sum/images/solutions/three_sum_solution_16.png new file mode 100644 index 00000000..b21ca0ad Binary files /dev/null and b/algorithms/two_pointers/three_sum/images/solutions/three_sum_solution_16.png differ diff --git a/algorithms/two_pointers/three_sum/images/solutions/three_sum_solution_17.png b/algorithms/two_pointers/three_sum/images/solutions/three_sum_solution_17.png new file mode 100644 index 00000000..63242bf1 Binary files /dev/null and b/algorithms/two_pointers/three_sum/images/solutions/three_sum_solution_17.png differ diff --git a/algorithms/two_pointers/three_sum/images/solutions/three_sum_solution_18.png b/algorithms/two_pointers/three_sum/images/solutions/three_sum_solution_18.png new file mode 100644 index 00000000..c6bb40cf Binary files /dev/null and b/algorithms/two_pointers/three_sum/images/solutions/three_sum_solution_18.png differ diff --git a/algorithms/two_pointers/three_sum/images/solutions/three_sum_solution_19.png b/algorithms/two_pointers/three_sum/images/solutions/three_sum_solution_19.png new file mode 100644 index 00000000..80bcf26f Binary files /dev/null and b/algorithms/two_pointers/three_sum/images/solutions/three_sum_solution_19.png differ diff --git a/algorithms/two_pointers/three_sum/images/solutions/three_sum_solution_2.png b/algorithms/two_pointers/three_sum/images/solutions/three_sum_solution_2.png new file mode 100644 index 00000000..a0ba0420 Binary files /dev/null and b/algorithms/two_pointers/three_sum/images/solutions/three_sum_solution_2.png differ diff --git a/algorithms/two_pointers/three_sum/images/solutions/three_sum_solution_3.png b/algorithms/two_pointers/three_sum/images/solutions/three_sum_solution_3.png new file mode 100644 index 00000000..c7d487c7 Binary files /dev/null and b/algorithms/two_pointers/three_sum/images/solutions/three_sum_solution_3.png differ diff --git a/algorithms/two_pointers/three_sum/images/solutions/three_sum_solution_4.png b/algorithms/two_pointers/three_sum/images/solutions/three_sum_solution_4.png new file mode 100644 index 00000000..b9ff85c5 Binary files /dev/null and b/algorithms/two_pointers/three_sum/images/solutions/three_sum_solution_4.png differ diff --git a/algorithms/two_pointers/three_sum/images/solutions/three_sum_solution_5.png b/algorithms/two_pointers/three_sum/images/solutions/three_sum_solution_5.png new file mode 100644 index 00000000..18e063bf Binary files /dev/null and b/algorithms/two_pointers/three_sum/images/solutions/three_sum_solution_5.png differ diff --git a/algorithms/two_pointers/three_sum/images/solutions/three_sum_solution_6.png b/algorithms/two_pointers/three_sum/images/solutions/three_sum_solution_6.png new file mode 100644 index 00000000..48394623 Binary files /dev/null and b/algorithms/two_pointers/three_sum/images/solutions/three_sum_solution_6.png differ diff --git a/algorithms/two_pointers/three_sum/images/solutions/three_sum_solution_7.png b/algorithms/two_pointers/three_sum/images/solutions/three_sum_solution_7.png new file mode 100644 index 00000000..63bee6d2 Binary files /dev/null and b/algorithms/two_pointers/three_sum/images/solutions/three_sum_solution_7.png differ diff --git a/algorithms/two_pointers/three_sum/images/solutions/three_sum_solution_8.png b/algorithms/two_pointers/three_sum/images/solutions/three_sum_solution_8.png new file mode 100644 index 00000000..fbcd1e3e Binary files /dev/null and b/algorithms/two_pointers/three_sum/images/solutions/three_sum_solution_8.png differ diff --git a/algorithms/two_pointers/three_sum/images/solutions/three_sum_solution_9.png b/algorithms/two_pointers/three_sum/images/solutions/three_sum_solution_9.png new file mode 100644 index 00000000..c7d785d2 Binary files /dev/null and b/algorithms/two_pointers/three_sum/images/solutions/three_sum_solution_9.png differ diff --git a/algorithms/two_pointers/three_sum/test_three_sum.py b/algorithms/two_pointers/three_sum/test_three_sum.py index 529504a0..f2b41b8c 100644 --- a/algorithms/two_pointers/three_sum/test_three_sum.py +++ b/algorithms/two_pointers/three_sum/test_three_sum.py @@ -1,27 +1,23 @@ import unittest - +from typing import List +from parameterized import parameterized from algorithms.two_pointers.three_sum import three_sum +THREE_SUM_TEST_CASES = [ + ([-1, 0, 1, 2, -1, -4], [[-1, -1, 2], [-1, 0, 1]]), + ([0, 1, 1], []), + ([0, 0, 0], [[0, 0, 0]]), + ([-1, 0, 1, 2, -1, -1], [[-1, -1, 2], [-1, 0, 1]]), + ([-1, 0, 1, 2, -1, -4], [[-1, -1, 2], [-1, 0, 1]]), + ([-1, 0, 1, 2, -1, -4, 2], [[-1, -1, 2], [-1, 0, 1], [-4, 2, 2]]), + ([-1, -1, 0, 1, 1, 1, 2], [[-1, -1, 2], [-1, 0, 1]]), + ([-1, 0, 1, 2, -1, -4, -1, 2, 1], [[-1, -1, 2], [-1, 0, 1], [-4, 2, 2]]), +] -class ThreeSumTestCases(unittest.TestCase): - def test_one(self): - """Should return [[-1, -1, 2], [-1, 0, 1]] for nums = [-1, 0, 1, 2, -1, -4]""" - nums = [-1, 0, 1, 2, -1, -4] - expected = [[-1, -1, 2], [-1, 0, 1]] - actual = three_sum(nums) - self.assertEqual(expected, actual) - - def test_two(self): - """Should return [] for nums = [0, 1, 1]""" - nums = [0, 1, 1] - expected = [] - actual = three_sum(nums) - self.assertEqual(expected, actual) - def test_three(self): - """Should return [[0,0,0]] for nums = [0,0,0]""" - nums = [0, 0, 0] - expected = [[0, 0, 0]] +class ThreeSumTestCases(unittest.TestCase): + @parameterized.expand(THREE_SUM_TEST_CASES) + def test_three_sum(self, nums: List[int], expected: List[List[int]]): actual = three_sum(nums) self.assertEqual(expected, actual) diff --git a/algorithms/two_pointers/triangle_numbers/README.md b/algorithms/two_pointers/triangle_numbers/README.md new file mode 100644 index 00000000..364e6dd2 --- /dev/null +++ b/algorithms/two_pointers/triangle_numbers/README.md @@ -0,0 +1,83 @@ +# Triangle Numbers + +Write a function to count the number of triplets in an integer array nums that could form the sides of a triangle. For +three sides to form a valid triangle, the sum of any two sides must be greater than the third side. The triplets do not +need to be unique. + +## Examples + +```text +Input: +nums = [11,4,9,6,15,18] + +Output: +10 + +Explanation: Valid combinations are... + +4, 15, 18 +6, 15, 18 +9, 15, 18 +11, 15, 18 +9, 11, 18 +6, 11, 15 +9, 11, 15 +4, 6, 9 +``` + +## Solution + +In order for a triplet to be valid lengths of a triangle, the sum of any two sides must be greater than the third side. +By sorting the array, we can leverage the two-pointer technique to count all valid triplets in O(n2) time and O(1) space. +The key to this question is realizing that if we sort three numbers from smallest to largest (say a ≤ b ≤ c), we only +need to check if a + b > c. If this condition holds, the other two conditions (a + c > b and b + c > a) are automatically +satisfied because c ≥ b and b ≥ a. For example, with 4, 8, 9, if 4 + 8 > 9 is true, then we have a valid triplet. + +![Solution 1](./images/solutions/triangle_numbers_solution_1.png) + +But not only that, triplets where the smallest number is between 4 and 8 are also valid triplets. + +![Solution 2](./images/solutions/triangle_numbers_solution_2.png) + +This means that if we sort the input array, and then iterate from the end of the array to the beginning, we can use the +two-pointer technique to efficiently count all valid triplets. + +![Solution 3](./images/solutions/triangle_numbers_solution_3.png) + +The pointers i, left, and right represent the current triplet we are considering. If nums[left] + nums[right] > nums[i] +then we know there are a total of right - left valid triplets, since all triplets between left and right are also valid +triplets. We can then decrement right to check for the valid triplets that can be made by decreasing the middle value. + +![Solution 4](./images/solutions/triangle_numbers_solution_4.png) +![Solution 5](./images/solutions/triangle_numbers_solution_5.png) +![Solution 6](./images/solutions/triangle_numbers_solution_6.png) +![Solution 7](./images/solutions/triangle_numbers_solution_7.png) + +When nums[left] + nums[right] < nums[i], we know that all triplets between left and right are also invalid, so we +increment left to look for a larger smallest value. + +![Solution 8](./images/solutions/triangle_numbers_solution_8.png) + +Each time left and right cross, we decrement i and reset left and right to their positions at opposite ends of the array. +This happens until i is less than 2, at which point we have counted all valid triplets. + +![Solution 9](./images/solutions/triangle_numbers_solution_9.png) +![Solution 10](./images/solutions/triangle_numbers_solution_10.png) +![Solution 11](./images/solutions/triangle_numbers_solution_11.png) +![Solution 12](./images/solutions/triangle_numbers_solution_12.png) +![Solution 13](./images/solutions/triangle_numbers_solution_13.png) +![Solution 14](./images/solutions/triangle_numbers_solution_14.png) +![Solution 15](./images/solutions/triangle_numbers_solution_15.png) +![Solution 16](./images/solutions/triangle_numbers_solution_16.png) +![Solution 17](./images/solutions/triangle_numbers_solution_17.png) +![Solution 18](./images/solutions/triangle_numbers_solution_18.png) +![Solution 19](./images/solutions/triangle_numbers_solution_19.png) +![Solution 20](./images/solutions/triangle_numbers_solution_20.png) +![Solution 21](./images/solutions/triangle_numbers_solution_21.png) +![Solution 22](./images/solutions/triangle_numbers_solution_22.png) +![Solution 23](./images/solutions/triangle_numbers_solution_23.png) +![Solution 24](./images/solutions/triangle_numbers_solution_24.png) +![Solution 25](./images/solutions/triangle_numbers_solution_25.png) +![Solution 26](./images/solutions/triangle_numbers_solution_26.png) +![Solution 27](./images/solutions/triangle_numbers_solution_27.png) +![Solution 28](./images/solutions/triangle_numbers_solution_28.png) diff --git a/algorithms/two_pointers/triangle_numbers/__init__.py b/algorithms/two_pointers/triangle_numbers/__init__.py new file mode 100644 index 00000000..2d40373c --- /dev/null +++ b/algorithms/two_pointers/triangle_numbers/__init__.py @@ -0,0 +1,51 @@ +from typing import List + + +def triangle_number(heights: List[int]) -> int: + """ + Finds the count of valid triangles that can be formed from the given input of numbers. A valid triangle is a triangle + which has any two sides whose sum is greater than the third side. This assumes that it is okay to manipulate the + input list. Therefore callers of this function should be aware that the input list is manipulated in place. + + Args: + heights (list): list of integers that represent sides of a triangle + Returns: + int: number of valid triangles that can be formed + """ + # If there are no heights, or we have an empty list, return 0 early as no valid triangles can be formed here. + if not heights: + return 0 + + # Sorts the heights in place. This incurs a time complexity cost of O(n log(n)) and space cost of O(n) as this sorting + # requires temporary storage using Python's timsort + heights.sort() + + # Keeps track of number of valid triangles that can be formed + count = 0 + + # Iterate through the list starting from the back, idx will be at the last position, this will be the third pointer + for idx in range(len(heights) - 1, 1, -1): + # Initialize the two pointers to keep track of the other two indices that will point to the two other numbers that + # can form a valid triangle. + left = 0 + right = idx - 1 + + # This is a micro-optimization to get the largest side and use it in the loop below + largest_side = heights[idx] + + while left < right: + # A valid triplet is found by satisfying the condition a + b > c. If this condition holds, then the other + # two conditions hold as well, a + c > b and b + c > a. + is_valid_triplet = heights[left] + heights[right] > largest_side + if is_valid_triplet: + # The numbers between the right and left pointers form valid triplets with the number at the idx position + # we find all the possible triplets(triangles) that can be formed by finding the difference. + count += right - left + # we decrement right to check if there are valid triplets that can be formed by decreasing the middle valid + right -= 1 + else: + # Increase the left to find the next maximum minimum number that can form a valid triplet + left += 1 + + # return the count of the triangles that can be formed + return count diff --git a/algorithms/two_pointers/triangle_numbers/images/solutions/triangle_numbers_solution_1.png b/algorithms/two_pointers/triangle_numbers/images/solutions/triangle_numbers_solution_1.png new file mode 100644 index 00000000..bc3fd587 Binary files /dev/null and b/algorithms/two_pointers/triangle_numbers/images/solutions/triangle_numbers_solution_1.png differ diff --git a/algorithms/two_pointers/triangle_numbers/images/solutions/triangle_numbers_solution_10.png b/algorithms/two_pointers/triangle_numbers/images/solutions/triangle_numbers_solution_10.png new file mode 100644 index 00000000..96a8b353 Binary files /dev/null and b/algorithms/two_pointers/triangle_numbers/images/solutions/triangle_numbers_solution_10.png differ diff --git a/algorithms/two_pointers/triangle_numbers/images/solutions/triangle_numbers_solution_11.png b/algorithms/two_pointers/triangle_numbers/images/solutions/triangle_numbers_solution_11.png new file mode 100644 index 00000000..99a457d3 Binary files /dev/null and b/algorithms/two_pointers/triangle_numbers/images/solutions/triangle_numbers_solution_11.png differ diff --git a/algorithms/two_pointers/triangle_numbers/images/solutions/triangle_numbers_solution_12.png b/algorithms/two_pointers/triangle_numbers/images/solutions/triangle_numbers_solution_12.png new file mode 100644 index 00000000..d9f4a2f3 Binary files /dev/null and b/algorithms/two_pointers/triangle_numbers/images/solutions/triangle_numbers_solution_12.png differ diff --git a/algorithms/two_pointers/triangle_numbers/images/solutions/triangle_numbers_solution_13.png b/algorithms/two_pointers/triangle_numbers/images/solutions/triangle_numbers_solution_13.png new file mode 100644 index 00000000..c34ced6e Binary files /dev/null and b/algorithms/two_pointers/triangle_numbers/images/solutions/triangle_numbers_solution_13.png differ diff --git a/algorithms/two_pointers/triangle_numbers/images/solutions/triangle_numbers_solution_14.png b/algorithms/two_pointers/triangle_numbers/images/solutions/triangle_numbers_solution_14.png new file mode 100644 index 00000000..024ccc2e Binary files /dev/null and b/algorithms/two_pointers/triangle_numbers/images/solutions/triangle_numbers_solution_14.png differ diff --git a/algorithms/two_pointers/triangle_numbers/images/solutions/triangle_numbers_solution_15.png b/algorithms/two_pointers/triangle_numbers/images/solutions/triangle_numbers_solution_15.png new file mode 100644 index 00000000..bb7e0333 Binary files /dev/null and b/algorithms/two_pointers/triangle_numbers/images/solutions/triangle_numbers_solution_15.png differ diff --git a/algorithms/two_pointers/triangle_numbers/images/solutions/triangle_numbers_solution_16.png b/algorithms/two_pointers/triangle_numbers/images/solutions/triangle_numbers_solution_16.png new file mode 100644 index 00000000..f7465ea3 Binary files /dev/null and b/algorithms/two_pointers/triangle_numbers/images/solutions/triangle_numbers_solution_16.png differ diff --git a/algorithms/two_pointers/triangle_numbers/images/solutions/triangle_numbers_solution_17.png b/algorithms/two_pointers/triangle_numbers/images/solutions/triangle_numbers_solution_17.png new file mode 100644 index 00000000..6da032bb Binary files /dev/null and b/algorithms/two_pointers/triangle_numbers/images/solutions/triangle_numbers_solution_17.png differ diff --git a/algorithms/two_pointers/triangle_numbers/images/solutions/triangle_numbers_solution_18.png b/algorithms/two_pointers/triangle_numbers/images/solutions/triangle_numbers_solution_18.png new file mode 100644 index 00000000..45fddeac Binary files /dev/null and b/algorithms/two_pointers/triangle_numbers/images/solutions/triangle_numbers_solution_18.png differ diff --git a/algorithms/two_pointers/triangle_numbers/images/solutions/triangle_numbers_solution_19.png b/algorithms/two_pointers/triangle_numbers/images/solutions/triangle_numbers_solution_19.png new file mode 100644 index 00000000..26cf2117 Binary files /dev/null and b/algorithms/two_pointers/triangle_numbers/images/solutions/triangle_numbers_solution_19.png differ diff --git a/algorithms/two_pointers/triangle_numbers/images/solutions/triangle_numbers_solution_2.png b/algorithms/two_pointers/triangle_numbers/images/solutions/triangle_numbers_solution_2.png new file mode 100644 index 00000000..a4e3082f Binary files /dev/null and b/algorithms/two_pointers/triangle_numbers/images/solutions/triangle_numbers_solution_2.png differ diff --git a/algorithms/two_pointers/triangle_numbers/images/solutions/triangle_numbers_solution_20.png b/algorithms/two_pointers/triangle_numbers/images/solutions/triangle_numbers_solution_20.png new file mode 100644 index 00000000..5b5234af Binary files /dev/null and b/algorithms/two_pointers/triangle_numbers/images/solutions/triangle_numbers_solution_20.png differ diff --git a/algorithms/two_pointers/triangle_numbers/images/solutions/triangle_numbers_solution_21.png b/algorithms/two_pointers/triangle_numbers/images/solutions/triangle_numbers_solution_21.png new file mode 100644 index 00000000..0ab75cd3 Binary files /dev/null and b/algorithms/two_pointers/triangle_numbers/images/solutions/triangle_numbers_solution_21.png differ diff --git a/algorithms/two_pointers/triangle_numbers/images/solutions/triangle_numbers_solution_22.png b/algorithms/two_pointers/triangle_numbers/images/solutions/triangle_numbers_solution_22.png new file mode 100644 index 00000000..7277dd49 Binary files /dev/null and b/algorithms/two_pointers/triangle_numbers/images/solutions/triangle_numbers_solution_22.png differ diff --git a/algorithms/two_pointers/triangle_numbers/images/solutions/triangle_numbers_solution_23.png b/algorithms/two_pointers/triangle_numbers/images/solutions/triangle_numbers_solution_23.png new file mode 100644 index 00000000..03dd57c4 Binary files /dev/null and b/algorithms/two_pointers/triangle_numbers/images/solutions/triangle_numbers_solution_23.png differ diff --git a/algorithms/two_pointers/triangle_numbers/images/solutions/triangle_numbers_solution_24.png b/algorithms/two_pointers/triangle_numbers/images/solutions/triangle_numbers_solution_24.png new file mode 100644 index 00000000..098f17ec Binary files /dev/null and b/algorithms/two_pointers/triangle_numbers/images/solutions/triangle_numbers_solution_24.png differ diff --git a/algorithms/two_pointers/triangle_numbers/images/solutions/triangle_numbers_solution_25.png b/algorithms/two_pointers/triangle_numbers/images/solutions/triangle_numbers_solution_25.png new file mode 100644 index 00000000..4681966d Binary files /dev/null and b/algorithms/two_pointers/triangle_numbers/images/solutions/triangle_numbers_solution_25.png differ diff --git a/algorithms/two_pointers/triangle_numbers/images/solutions/triangle_numbers_solution_26.png b/algorithms/two_pointers/triangle_numbers/images/solutions/triangle_numbers_solution_26.png new file mode 100644 index 00000000..60635a34 Binary files /dev/null and b/algorithms/two_pointers/triangle_numbers/images/solutions/triangle_numbers_solution_26.png differ diff --git a/algorithms/two_pointers/triangle_numbers/images/solutions/triangle_numbers_solution_27.png b/algorithms/two_pointers/triangle_numbers/images/solutions/triangle_numbers_solution_27.png new file mode 100644 index 00000000..e12b9a4c Binary files /dev/null and b/algorithms/two_pointers/triangle_numbers/images/solutions/triangle_numbers_solution_27.png differ diff --git a/algorithms/two_pointers/triangle_numbers/images/solutions/triangle_numbers_solution_28.png b/algorithms/two_pointers/triangle_numbers/images/solutions/triangle_numbers_solution_28.png new file mode 100644 index 00000000..8c6078e3 Binary files /dev/null and b/algorithms/two_pointers/triangle_numbers/images/solutions/triangle_numbers_solution_28.png differ diff --git a/algorithms/two_pointers/triangle_numbers/images/solutions/triangle_numbers_solution_3.png b/algorithms/two_pointers/triangle_numbers/images/solutions/triangle_numbers_solution_3.png new file mode 100644 index 00000000..e64fb8ef Binary files /dev/null and b/algorithms/two_pointers/triangle_numbers/images/solutions/triangle_numbers_solution_3.png differ diff --git a/algorithms/two_pointers/triangle_numbers/images/solutions/triangle_numbers_solution_4.png b/algorithms/two_pointers/triangle_numbers/images/solutions/triangle_numbers_solution_4.png new file mode 100644 index 00000000..c274ea00 Binary files /dev/null and b/algorithms/two_pointers/triangle_numbers/images/solutions/triangle_numbers_solution_4.png differ diff --git a/algorithms/two_pointers/triangle_numbers/images/solutions/triangle_numbers_solution_5.png b/algorithms/two_pointers/triangle_numbers/images/solutions/triangle_numbers_solution_5.png new file mode 100644 index 00000000..236e1552 Binary files /dev/null and b/algorithms/two_pointers/triangle_numbers/images/solutions/triangle_numbers_solution_5.png differ diff --git a/algorithms/two_pointers/triangle_numbers/images/solutions/triangle_numbers_solution_6.png b/algorithms/two_pointers/triangle_numbers/images/solutions/triangle_numbers_solution_6.png new file mode 100644 index 00000000..8c9c87ab Binary files /dev/null and b/algorithms/two_pointers/triangle_numbers/images/solutions/triangle_numbers_solution_6.png differ diff --git a/algorithms/two_pointers/triangle_numbers/images/solutions/triangle_numbers_solution_7.png b/algorithms/two_pointers/triangle_numbers/images/solutions/triangle_numbers_solution_7.png new file mode 100644 index 00000000..b97cd187 Binary files /dev/null and b/algorithms/two_pointers/triangle_numbers/images/solutions/triangle_numbers_solution_7.png differ diff --git a/algorithms/two_pointers/triangle_numbers/images/solutions/triangle_numbers_solution_8.png b/algorithms/two_pointers/triangle_numbers/images/solutions/triangle_numbers_solution_8.png new file mode 100644 index 00000000..d505a1f3 Binary files /dev/null and b/algorithms/two_pointers/triangle_numbers/images/solutions/triangle_numbers_solution_8.png differ diff --git a/algorithms/two_pointers/triangle_numbers/images/solutions/triangle_numbers_solution_9.png b/algorithms/two_pointers/triangle_numbers/images/solutions/triangle_numbers_solution_9.png new file mode 100644 index 00000000..9b9c9773 Binary files /dev/null and b/algorithms/two_pointers/triangle_numbers/images/solutions/triangle_numbers_solution_9.png differ diff --git a/algorithms/two_pointers/triangle_numbers/test_triangle_numbers.py b/algorithms/two_pointers/triangle_numbers/test_triangle_numbers.py new file mode 100644 index 00000000..c5108d3e --- /dev/null +++ b/algorithms/two_pointers/triangle_numbers/test_triangle_numbers.py @@ -0,0 +1,21 @@ +import unittest +from typing import List +from parameterized import parameterized +from algorithms.two_pointers.triangle_numbers import triangle_number + +TRIANGLE_NUMBER_TEST_CASES = [ + ([11, 4, 9, 6, 15, 18], 10), + ([2, 2, 3, 4], 3), + ([4, 2, 3, 4], 4), +] + + +class TriangleNumberTestCases(unittest.TestCase): + @parameterized.expand(TRIANGLE_NUMBER_TEST_CASES) + def test_triangle_number(self, heights: List[int], expected: int): + actual = triangle_number(heights) + self.assertEqual(expected, actual) + + +if __name__ == "__main__": + unittest.main() diff --git a/algorithms/arrays/two_sum/README.md b/algorithms/two_pointers/two_sum/README.md similarity index 72% rename from algorithms/arrays/two_sum/README.md rename to algorithms/two_pointers/two_sum/README.md index b5b6a601..f439f1e8 100644 --- a/algorithms/arrays/two_sum/README.md +++ b/algorithms/two_pointers/two_sum/README.md @@ -51,9 +51,9 @@ equals k. Return TRUE if such elements exist or FALSE otherwise. ## Examples -![Example 1](./images/examples/two_sum_4_input_is_bst_example_1.png) -![Example 2](./images/examples/two_sum_4_input_is_bst_example_2.png) -![Example 3](./images/examples/two_sum_4_input_is_bst_example_3.png) +![Example 1](images/examples/two_sum_4_input_is_bst_example_1.png) +![Example 2](images/examples/two_sum_4_input_is_bst_example_2.png) +![Example 3](images/examples/two_sum_4_input_is_bst_example_3.png) ## Solution @@ -78,20 +78,20 @@ Using the intuition above, the solution can be implemented as follows: Let’s look at the following illustration to get a better understanding of the solution: -![Solution 1](./images/solutions/two_sum_4_input_is_bst_solution_1.png) -![Solution 2](./images/solutions/two_sum_4_input_is_bst_solution_2.png) -![Solution 3](./images/solutions/two_sum_4_input_is_bst_solution_3.png) -![Solution 4](./images/solutions/two_sum_4_input_is_bst_solution_4.png) -![Solution 5](./images/solutions/two_sum_4_input_is_bst_solution_5.png) -![Solution 6](./images/solutions/two_sum_4_input_is_bst_solution_6.png) -![Solution 7](./images/solutions/two_sum_4_input_is_bst_solution_7.png) -![Solution 8](./images/solutions/two_sum_4_input_is_bst_solution_8.png) -![Solution 9](./images/solutions/two_sum_4_input_is_bst_solution_9.png) -![Solution 10](./images/solutions/two_sum_4_input_is_bst_solution_10.png) -![Solution 11](./images/solutions/two_sum_4_input_is_bst_solution_11.png) -![Solution 12](./images/solutions/two_sum_4_input_is_bst_solution_12.png) -![Solution 13](./images/solutions/two_sum_4_input_is_bst_solution_13.png) -![Solution 14](./images/solutions/two_sum_4_input_is_bst_solution_14.png) +![Solution 1](images/solutions/two_sum_4_input_is_bst_solution_1.png) +![Solution 2](images/solutions/two_sum_4_input_is_bst_solution_2.png) +![Solution 3](images/solutions/two_sum_4_input_is_bst_solution_3.png) +![Solution 4](images/solutions/two_sum_4_input_is_bst_solution_4.png) +![Solution 5](images/solutions/two_sum_4_input_is_bst_solution_5.png) +![Solution 6](images/solutions/two_sum_4_input_is_bst_solution_6.png) +![Solution 7](images/solutions/two_sum_4_input_is_bst_solution_7.png) +![Solution 8](images/solutions/two_sum_4_input_is_bst_solution_8.png) +![Solution 9](images/solutions/two_sum_4_input_is_bst_solution_9.png) +![Solution 10](images/solutions/two_sum_4_input_is_bst_solution_10.png) +![Solution 11](images/solutions/two_sum_4_input_is_bst_solution_11.png) +![Solution 12](images/solutions/two_sum_4_input_is_bst_solution_12.png) +![Solution 13](images/solutions/two_sum_4_input_is_bst_solution_13.png) +![Solution 14](images/solutions/two_sum_4_input_is_bst_solution_14.png) ### Time Complexity diff --git a/algorithms/arrays/two_sum/__init__.py b/algorithms/two_pointers/two_sum/__init__.py similarity index 100% rename from algorithms/arrays/two_sum/__init__.py rename to algorithms/two_pointers/two_sum/__init__.py diff --git a/algorithms/arrays/two_sum/images/examples/two_sum_4_input_is_bst_example_1.png b/algorithms/two_pointers/two_sum/images/examples/two_sum_4_input_is_bst_example_1.png similarity index 100% rename from algorithms/arrays/two_sum/images/examples/two_sum_4_input_is_bst_example_1.png rename to algorithms/two_pointers/two_sum/images/examples/two_sum_4_input_is_bst_example_1.png diff --git a/algorithms/arrays/two_sum/images/examples/two_sum_4_input_is_bst_example_2.png b/algorithms/two_pointers/two_sum/images/examples/two_sum_4_input_is_bst_example_2.png similarity index 100% rename from algorithms/arrays/two_sum/images/examples/two_sum_4_input_is_bst_example_2.png rename to algorithms/two_pointers/two_sum/images/examples/two_sum_4_input_is_bst_example_2.png diff --git a/algorithms/arrays/two_sum/images/examples/two_sum_4_input_is_bst_example_3.png b/algorithms/two_pointers/two_sum/images/examples/two_sum_4_input_is_bst_example_3.png similarity index 100% rename from algorithms/arrays/two_sum/images/examples/two_sum_4_input_is_bst_example_3.png rename to algorithms/two_pointers/two_sum/images/examples/two_sum_4_input_is_bst_example_3.png diff --git a/algorithms/arrays/two_sum/images/solutions/two_sum_4_input_is_bst_solution_1.png b/algorithms/two_pointers/two_sum/images/solutions/two_sum_4_input_is_bst_solution_1.png similarity index 100% rename from algorithms/arrays/two_sum/images/solutions/two_sum_4_input_is_bst_solution_1.png rename to algorithms/two_pointers/two_sum/images/solutions/two_sum_4_input_is_bst_solution_1.png diff --git a/algorithms/arrays/two_sum/images/solutions/two_sum_4_input_is_bst_solution_10.png b/algorithms/two_pointers/two_sum/images/solutions/two_sum_4_input_is_bst_solution_10.png similarity index 100% rename from algorithms/arrays/two_sum/images/solutions/two_sum_4_input_is_bst_solution_10.png rename to algorithms/two_pointers/two_sum/images/solutions/two_sum_4_input_is_bst_solution_10.png diff --git a/algorithms/arrays/two_sum/images/solutions/two_sum_4_input_is_bst_solution_11.png b/algorithms/two_pointers/two_sum/images/solutions/two_sum_4_input_is_bst_solution_11.png similarity index 100% rename from algorithms/arrays/two_sum/images/solutions/two_sum_4_input_is_bst_solution_11.png rename to algorithms/two_pointers/two_sum/images/solutions/two_sum_4_input_is_bst_solution_11.png diff --git a/algorithms/arrays/two_sum/images/solutions/two_sum_4_input_is_bst_solution_12.png b/algorithms/two_pointers/two_sum/images/solutions/two_sum_4_input_is_bst_solution_12.png similarity index 100% rename from algorithms/arrays/two_sum/images/solutions/two_sum_4_input_is_bst_solution_12.png rename to algorithms/two_pointers/two_sum/images/solutions/two_sum_4_input_is_bst_solution_12.png diff --git a/algorithms/arrays/two_sum/images/solutions/two_sum_4_input_is_bst_solution_13.png b/algorithms/two_pointers/two_sum/images/solutions/two_sum_4_input_is_bst_solution_13.png similarity index 100% rename from algorithms/arrays/two_sum/images/solutions/two_sum_4_input_is_bst_solution_13.png rename to algorithms/two_pointers/two_sum/images/solutions/two_sum_4_input_is_bst_solution_13.png diff --git a/algorithms/arrays/two_sum/images/solutions/two_sum_4_input_is_bst_solution_14.png b/algorithms/two_pointers/two_sum/images/solutions/two_sum_4_input_is_bst_solution_14.png similarity index 100% rename from algorithms/arrays/two_sum/images/solutions/two_sum_4_input_is_bst_solution_14.png rename to algorithms/two_pointers/two_sum/images/solutions/two_sum_4_input_is_bst_solution_14.png diff --git a/algorithms/arrays/two_sum/images/solutions/two_sum_4_input_is_bst_solution_2.png b/algorithms/two_pointers/two_sum/images/solutions/two_sum_4_input_is_bst_solution_2.png similarity index 100% rename from algorithms/arrays/two_sum/images/solutions/two_sum_4_input_is_bst_solution_2.png rename to algorithms/two_pointers/two_sum/images/solutions/two_sum_4_input_is_bst_solution_2.png diff --git a/algorithms/arrays/two_sum/images/solutions/two_sum_4_input_is_bst_solution_3.png b/algorithms/two_pointers/two_sum/images/solutions/two_sum_4_input_is_bst_solution_3.png similarity index 100% rename from algorithms/arrays/two_sum/images/solutions/two_sum_4_input_is_bst_solution_3.png rename to algorithms/two_pointers/two_sum/images/solutions/two_sum_4_input_is_bst_solution_3.png diff --git a/algorithms/arrays/two_sum/images/solutions/two_sum_4_input_is_bst_solution_4.png b/algorithms/two_pointers/two_sum/images/solutions/two_sum_4_input_is_bst_solution_4.png similarity index 100% rename from algorithms/arrays/two_sum/images/solutions/two_sum_4_input_is_bst_solution_4.png rename to algorithms/two_pointers/two_sum/images/solutions/two_sum_4_input_is_bst_solution_4.png diff --git a/algorithms/arrays/two_sum/images/solutions/two_sum_4_input_is_bst_solution_5.png b/algorithms/two_pointers/two_sum/images/solutions/two_sum_4_input_is_bst_solution_5.png similarity index 100% rename from algorithms/arrays/two_sum/images/solutions/two_sum_4_input_is_bst_solution_5.png rename to algorithms/two_pointers/two_sum/images/solutions/two_sum_4_input_is_bst_solution_5.png diff --git a/algorithms/arrays/two_sum/images/solutions/two_sum_4_input_is_bst_solution_6.png b/algorithms/two_pointers/two_sum/images/solutions/two_sum_4_input_is_bst_solution_6.png similarity index 100% rename from algorithms/arrays/two_sum/images/solutions/two_sum_4_input_is_bst_solution_6.png rename to algorithms/two_pointers/two_sum/images/solutions/two_sum_4_input_is_bst_solution_6.png diff --git a/algorithms/arrays/two_sum/images/solutions/two_sum_4_input_is_bst_solution_7.png b/algorithms/two_pointers/two_sum/images/solutions/two_sum_4_input_is_bst_solution_7.png similarity index 100% rename from algorithms/arrays/two_sum/images/solutions/two_sum_4_input_is_bst_solution_7.png rename to algorithms/two_pointers/two_sum/images/solutions/two_sum_4_input_is_bst_solution_7.png diff --git a/algorithms/arrays/two_sum/images/solutions/two_sum_4_input_is_bst_solution_8.png b/algorithms/two_pointers/two_sum/images/solutions/two_sum_4_input_is_bst_solution_8.png similarity index 100% rename from algorithms/arrays/two_sum/images/solutions/two_sum_4_input_is_bst_solution_8.png rename to algorithms/two_pointers/two_sum/images/solutions/two_sum_4_input_is_bst_solution_8.png diff --git a/algorithms/arrays/two_sum/images/solutions/two_sum_4_input_is_bst_solution_9.png b/algorithms/two_pointers/two_sum/images/solutions/two_sum_4_input_is_bst_solution_9.png similarity index 100% rename from algorithms/arrays/two_sum/images/solutions/two_sum_4_input_is_bst_solution_9.png rename to algorithms/two_pointers/two_sum/images/solutions/two_sum_4_input_is_bst_solution_9.png diff --git a/algorithms/arrays/two_sum/test_two_sum.py b/algorithms/two_pointers/two_sum/test_two_sum.py similarity index 97% rename from algorithms/arrays/two_sum/test_two_sum.py rename to algorithms/two_pointers/two_sum/test_two_sum.py index 09b08070..0c6de3e5 100644 --- a/algorithms/arrays/two_sum/test_two_sum.py +++ b/algorithms/two_pointers/two_sum/test_two_sum.py @@ -2,7 +2,7 @@ from typing import List from parameterized import parameterized from datastructures.trees.binary.node import BinaryTreeNode -from algorithms.arrays.two_sum import ( +from algorithms.two_pointers.two_sum import ( two_sum, two_sum_with_pointers, two_sum_find_target, diff --git a/algorithms/arrays/two_sum_less_k/README.md b/algorithms/two_pointers/two_sum_less_k/README.md similarity index 58% rename from algorithms/arrays/two_sum_less_k/README.md rename to algorithms/two_pointers/two_sum_less_k/README.md index cdefe60f..e641afee 100644 --- a/algorithms/arrays/two_sum_less_k/README.md +++ b/algorithms/two_pointers/two_sum_less_k/README.md @@ -11,10 +11,10 @@ Constraints ## Examples -![Example 1](./images/examples/two_sum_less_k_example_1.png) -![Example 2](./images/examples/two_sum_less_k_example_2.png) -![Example 3](./images/examples/two_sum_less_k_example_3.png) -![Example 4](./images/examples/two_sum_less_k_example_4.png) +![Example 1](images/examples/two_sum_less_k_example_1.png) +![Example 2](images/examples/two_sum_less_k_example_2.png) +![Example 3](images/examples/two_sum_less_k_example_3.png) +![Example 4](images/examples/two_sum_less_k_example_4.png) ## Related Topics diff --git a/algorithms/arrays/two_sum_less_k/__init__.py b/algorithms/two_pointers/two_sum_less_k/__init__.py similarity index 100% rename from algorithms/arrays/two_sum_less_k/__init__.py rename to algorithms/two_pointers/two_sum_less_k/__init__.py diff --git a/algorithms/arrays/two_sum_less_k/images/examples/two_sum_less_k_example_1.png b/algorithms/two_pointers/two_sum_less_k/images/examples/two_sum_less_k_example_1.png similarity index 100% rename from algorithms/arrays/two_sum_less_k/images/examples/two_sum_less_k_example_1.png rename to algorithms/two_pointers/two_sum_less_k/images/examples/two_sum_less_k_example_1.png diff --git a/algorithms/arrays/two_sum_less_k/images/examples/two_sum_less_k_example_2.png b/algorithms/two_pointers/two_sum_less_k/images/examples/two_sum_less_k_example_2.png similarity index 100% rename from algorithms/arrays/two_sum_less_k/images/examples/two_sum_less_k_example_2.png rename to algorithms/two_pointers/two_sum_less_k/images/examples/two_sum_less_k_example_2.png diff --git a/algorithms/arrays/two_sum_less_k/images/examples/two_sum_less_k_example_3.png b/algorithms/two_pointers/two_sum_less_k/images/examples/two_sum_less_k_example_3.png similarity index 100% rename from algorithms/arrays/two_sum_less_k/images/examples/two_sum_less_k_example_3.png rename to algorithms/two_pointers/two_sum_less_k/images/examples/two_sum_less_k_example_3.png diff --git a/algorithms/arrays/two_sum_less_k/images/examples/two_sum_less_k_example_4.png b/algorithms/two_pointers/two_sum_less_k/images/examples/two_sum_less_k_example_4.png similarity index 100% rename from algorithms/arrays/two_sum_less_k/images/examples/two_sum_less_k_example_4.png rename to algorithms/two_pointers/two_sum_less_k/images/examples/two_sum_less_k_example_4.png diff --git a/algorithms/arrays/two_sum_less_k/test_two_sum.py b/algorithms/two_pointers/two_sum_less_k/test_two_sum.py similarity index 100% rename from algorithms/arrays/two_sum_less_k/test_two_sum.py rename to algorithms/two_pointers/two_sum_less_k/test_two_sum.py diff --git a/puzzles/arrays/move_zeroes/README.md b/puzzles/arrays/move_zeroes/README.md deleted file mode 100644 index 422ca1bd..00000000 --- a/puzzles/arrays/move_zeroes/README.md +++ /dev/null @@ -1,23 +0,0 @@ -# Move Zeroes - -Given an integer array nums, move all 0's to the end of it while maintaining the relative order of the non-zero -elements. - -Note that you must do this in-place without making a copy of the array. - -```plain - -Example 1: - -Input: nums = [0,1,0,3,12] -Output: [1,3,12,0,0] -Example 2: - -Input: nums = [0] -Output: [0] -``` - -## Related Topics - -- Array -- Two Pointers diff --git a/puzzles/arrays/move_zeroes/test_move_zeroes.py b/puzzles/arrays/move_zeroes/test_move_zeroes.py deleted file mode 100644 index 26743fa6..00000000 --- a/puzzles/arrays/move_zeroes/test_move_zeroes.py +++ /dev/null @@ -1,52 +0,0 @@ -import unittest -from . import move_zeroes, move_zeroes_one - - -class MoveZeroesTestCase(unittest.TestCase): - def test_0_1_0_3_12(self): - """should modify nums = [0, 1, 0, 3, 12] in place to [1,3,12,0,0]""" - nums = [0, 1, 0, 3, 12] - expected = [1, 3, 12, 0, 0] - move_zeroes(nums) - self.assertEqual(expected, nums) - - def test_0(self): - """should modify nums = [0] in place to [0]""" - nums = [0] - expected = [0] - move_zeroes(nums) - self.assertEqual(expected, nums) - - def test_1_0(self): - """should modify nums = [1, 0] in place to [1, 0]""" - nums = [1, 0] - expected = [1, 0] - move_zeroes(nums) - self.assertEqual(expected, nums) - - -class MoveZeroesOneTestCase(unittest.TestCase): - def test_0_1_0_3_12(self): - """should modify nums = [0, 1, 0, 3, 12] in place to [1,3,12,0,0]""" - nums = [0, 1, 0, 3, 12] - expected = [1, 3, 12, 0, 0] - move_zeroes_one(nums) - self.assertEqual(expected, nums) - - def test_0(self): - """should modify nums = [0] in place to [0]""" - nums = [0] - expected = [0] - move_zeroes_one(nums) - self.assertEqual(expected, nums) - - def test_1_0(self): - """should modify nums = [1, 0] in place to [1, 0]""" - nums = [1, 0] - expected = [1, 0] - move_zeroes_one(nums) - self.assertEqual(expected, nums) - - -if __name__ == "__main__": - unittest.main() diff --git a/puzzles/arrays/rain_water_trapped/README.md b/puzzles/arrays/rain_water_trapped/README.md deleted file mode 100644 index c348cc67..00000000 --- a/puzzles/arrays/rain_water_trapped/README.md +++ /dev/null @@ -1,44 +0,0 @@ -# Rain Water Trapped - -Given an integer array A of non-negative integers representing an elevation map where the width of each bar is 1, -compute how much water it is able to trap after raining. - -Input Format -The only argument given is integer array A. - -Output Format -Return the total water it is able to trap after raining. - -```plain -Example Input -Input 1: - -A = [0, 1, 0, 2, 1, 0, 1, 3, 2, 1, 2, 1] -Input 2: - -A = [1, 2] - -Example Output -Output 1: - -6 -Output 2: - -0 - -Example Explanation -Explanation 1: - -In this case, 6 units of rain water (blue section) are being trapped. -Explanation 2: - -No water is trapped. -``` - -## Related Topics - -- Array -- Two Pointers -- Dynamic Programming -- Stack -- Monotonic Stack \ No newline at end of file diff --git a/puzzles/arrays/rain_water_trapped/__init__.py b/puzzles/arrays/rain_water_trapped/__init__.py deleted file mode 100644 index f5edf958..00000000 --- a/puzzles/arrays/rain_water_trapped/__init__.py +++ /dev/null @@ -1,21 +0,0 @@ -from typing import List - - -def trapped_rain_water(heights: List[int]) -> int: - if not heights or len(heights) == 1: - return 0 - left, right = 0, len(heights) - 1 - result = 0 - mx, mi = 0, 0 - - while left <= right: - mi = min(heights[left], heights[right]) - mx = max(mx, mi) - - result += mx - mi - - if heights[left] < heights[right]: - left += 1 - else: - right -= 1 - return result diff --git a/puzzles/arrays/rain_water_trapped/test_trapped_rain_water.py b/puzzles/arrays/rain_water_trapped/test_trapped_rain_water.py deleted file mode 100644 index 6a45ed43..00000000 --- a/puzzles/arrays/rain_water_trapped/test_trapped_rain_water.py +++ /dev/null @@ -1,29 +0,0 @@ -import unittest -from . import trapped_rain_water - - -class TrappedRainWaterTestCase(unittest.TestCase): - def test_1(self): - """Should return 6 for input of [0, 1, 0, 2, 1, 0, 1, 3, 2, 1, 2, 1]""" - a = [0, 1, 0, 2, 1, 0, 1, 3, 2, 1, 2, 1] - expected = 6 - actual = trapped_rain_water(a) - self.assertEqual(expected, actual) - - def test_2(self): - """Should return 0 for input of [1, 2]""" - a = [1, 2] - expected = 0 - actual = trapped_rain_water(a) - self.assertEqual(expected, actual) - - def test_3(self): - """Should return 9 for input of [4,2,0,3,2,5]""" - a = [4, 2, 0, 3, 2, 5] - expected = 9 - actual = trapped_rain_water(a) - self.assertEqual(expected, actual) - - -if __name__ == "__main__": - unittest.main()