diff --git a/DIRECTORY.md b/DIRECTORY.md index 410c8120..0880b19a 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -345,6 +345,8 @@ * [Test Palindrome](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/two_pointers/palindrome/test_palindrome.py) * [Test Palindrome Index](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/two_pointers/palindrome/test_palindrome_index.py) * [Test Palindrome Pairs](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/two_pointers/palindrome/test_palindrome_pairs.py) + * Product Of Array Except Self + * [Test Product Except Self](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/two_pointers/product_of_array_except_self/test_product_except_self.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 @@ -756,8 +758,6 @@ * [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) - * 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) * 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/two_pointers/product_of_array_except_self/README.md b/algorithms/two_pointers/product_of_array_except_self/README.md new file mode 100644 index 00000000..74982725 --- /dev/null +++ b/algorithms/two_pointers/product_of_array_except_self/README.md @@ -0,0 +1,114 @@ +# Product of Array Except Self + +Given an integer array nums, return an array answer such that answer[i] is equal to the product of all the elements of +nums except nums[i]. + +The product of any prefix or suffix of nums is guaranteed to fit in a 32-bit integer. + +You must write an algorithm that runs in O(n) time and without using the division operation. + +Example 1: +```plain +Input: nums = [1,2,3,4] +Output: [24,12,8,6] +``` + +Example 2: +```text +Input: nums = [-1,1,0,-3,3] +Output: [0,0,9,0,0] +``` + +Example 3: +```text +Input: nums = [2,4,0,6] +Output: [0,0,48,0] +``` + +Example 4: +```text +Input: nums = [1, -3, 5, 7, -11] +Output: [1155, -385, 231, 165, -105] +``` + +## Related Topics + +- Array +- Prefix Sum + +## Solution + +The idea is that we can break down the problem into two parts: the product of elements to the left of each index and the +product of elements to the right of each index. By maintaining two separate running products as we traverse the array +from both ends, we can accumulate the product values to populate the res array. This approach eliminates the need for +repeated multiplications and effectively calculates the product except for the element at the current index. The Two +Pointers pattern is employed here to handle both the left and right products in a single traversal. + +Here’s how the algorithm works: + +- Initialize the following variables that will assist us in performing the algorithm: + - res: This array will be used to store the output. It is initialized to 1 + - l, r: These are the pointers used to traverse the array. They are initialized to the left and right ends of the + array, respectively. + - `left_product`: This variable stores the product of the elements to the left of the l pointer. + - `right_product`: This variable stores the product of the elements to the right of the r pointer. +- Traverse the array while the l and r pointers are not out of bounds: + - Calculate the product to the left of nums[l] and store it in res[l] using the following formula: + > res[l] = res[l] * left_product + - Calculate the product to the right of the nums[r] and store it in res[r] using the following formula: + > res[r] = res[r] * right_product + - Update left_product to include the current element, nums[l], in the accumulated product for the next iteration. + - Update right_product to include the current element, nums[r], in the accumulated product for the next iteration. + - Finally, increment the l pointer and decrement the r pointer to evaluate the next elements. + - The steps above are repeated until the l and r pointers go out of bounds. + + > - When l == r, both pointers point to the middle element of the array. For this element, both the products to its left + > and right are being computed one after another and stored in res[l] (or res[r] since l == r in this case). Therefore, + > for the case of the middle element, the final product of all the elements, excluding it, is computed in one step. + > - After the l and r pointers cross each other, the following behavior occurs: + > - The l pointer computes the product to the left of nums[l] as expected, but now the product to the right of + > nums[l] has already been computed in a previous iteration by the r pointer. Now both the right and left products + > have been calculated and combined, the resultant product in this entry is the final product of all the elements, + > excluding the current one. + > - The r pointer computes the product to the right of nums[r] as expected, but now the product to the left of nums[r] + > has already been computed in a previous iteration by the l pointer. Now both the right and left products have been + > calculated and combined, the resultant product in this entry is the final product of all the elements, excluding + > the current one. + +- Lastly, the res array contains the desired result, so return it. + +![Solution 1](./images/solutions/product_of_array_except_self_solution_1.png) +![Solution 2](./images/solutions/product_of_array_except_self_solution_2.png) +![Solution 3](./images/solutions/product_of_array_except_self_solution_3.png) +![Solution 4](./images/solutions/product_of_array_except_self_solution_4.png) +![Solution 5](./images/solutions/product_of_array_except_self_solution_5.png) +![Solution 6](./images/solutions/product_of_array_except_self_solution_6.png) +![Solution 7](./images/solutions/product_of_array_except_self_solution_7.png) +![Solution 8](./images/solutions/product_of_array_except_self_solution_8.png) +![Solution 9](./images/solutions/product_of_array_except_self_solution_9.png) +![Solution 10](./images/solutions/product_of_array_except_self_solution_10.png) +![Solution 11](./images/solutions/product_of_array_except_self_solution_11.png) +![Solution 12](./images/solutions/product_of_array_except_self_solution_12.png) +![Solution 13](./images/solutions/product_of_array_except_self_solution_13.png) + +### Solution Summary + +The solution can be summarized in the following steps: + +- Create a list with the same length as the input list, initialized with 1s. +- Keep track of products on the left and right sides of the current element. +- Use two pointers—one starting from the beginning and the other from the end of the list. +- Multiply and update values in the output array based on accumulated products and current element values. +- Move the pointers toward each other to process the entire list. +- + +### Time Complexity + +The time complexity of this solution is O(n), since both the pointers simultaneously traverse the length of the array +once. + +### Space Complexity + +The space complexity of this solution is O(1), since it doesn’t use any additional array for computations but only +constant additional space. + diff --git a/algorithms/two_pointers/product_of_array_except_self/__init__.py b/algorithms/two_pointers/product_of_array_except_self/__init__.py new file mode 100644 index 00000000..460704a6 --- /dev/null +++ b/algorithms/two_pointers/product_of_array_except_self/__init__.py @@ -0,0 +1,58 @@ +from typing import List + + +def product_except_self_prefix_sums(nums: List[int]) -> List[int]: + if len(nums) <= 1: + return nums + + result = [1] * len(nums) + prefix = 1 + + for i in range(len(nums)): + result[i] = prefix + prefix *= nums[i] + + postfix = 1 + for i in range(len(nums) - 1, -1, -1): + result[i] *= postfix + postfix *= nums[i] + + return result + + +def product_except_self_two_pointers(nums: List[int]) -> List[int]: + # Get the length of the input list + n = len(nums) + + # Initialize a result list with 1's for products + res = [1] * n + + # Initialize variables for left and right products + left_product, right_product = 1, 1 + + # Initialize pointers for the left and right ends of the list + l = 0 + r = n - 1 + + # Iterate through the list while moving the pointers towards each other + while l < n and r > -1: + # Update the result at the left index with the accumulated left product + res[l] *= left_product + + # Update the result at the right index with the accumulated right product + res[r] *= right_product + + # Update the left product with the current element's value + left_product *= nums[l] + + # Update the right product with the current element's value + right_product *= nums[r] + + # Move the left pointer to the right + l += 1 + + # Move the right pointer to the left + r -= 1 + + # Return the final product result list + return res diff --git a/algorithms/two_pointers/product_of_array_except_self/images/solutions/product_of_array_except_self_solution_1.png b/algorithms/two_pointers/product_of_array_except_self/images/solutions/product_of_array_except_self_solution_1.png new file mode 100644 index 00000000..3eb35b69 Binary files /dev/null and b/algorithms/two_pointers/product_of_array_except_self/images/solutions/product_of_array_except_self_solution_1.png differ diff --git a/algorithms/two_pointers/product_of_array_except_self/images/solutions/product_of_array_except_self_solution_10.png b/algorithms/two_pointers/product_of_array_except_self/images/solutions/product_of_array_except_self_solution_10.png new file mode 100644 index 00000000..2d399077 Binary files /dev/null and b/algorithms/two_pointers/product_of_array_except_self/images/solutions/product_of_array_except_self_solution_10.png differ diff --git a/algorithms/two_pointers/product_of_array_except_self/images/solutions/product_of_array_except_self_solution_11.png b/algorithms/two_pointers/product_of_array_except_self/images/solutions/product_of_array_except_self_solution_11.png new file mode 100644 index 00000000..6421285f Binary files /dev/null and b/algorithms/two_pointers/product_of_array_except_self/images/solutions/product_of_array_except_self_solution_11.png differ diff --git a/algorithms/two_pointers/product_of_array_except_self/images/solutions/product_of_array_except_self_solution_12.png b/algorithms/two_pointers/product_of_array_except_self/images/solutions/product_of_array_except_self_solution_12.png new file mode 100644 index 00000000..f9d17d1d Binary files /dev/null and b/algorithms/two_pointers/product_of_array_except_self/images/solutions/product_of_array_except_self_solution_12.png differ diff --git a/algorithms/two_pointers/product_of_array_except_self/images/solutions/product_of_array_except_self_solution_13.png b/algorithms/two_pointers/product_of_array_except_self/images/solutions/product_of_array_except_self_solution_13.png new file mode 100644 index 00000000..bc68d118 Binary files /dev/null and b/algorithms/two_pointers/product_of_array_except_self/images/solutions/product_of_array_except_self_solution_13.png differ diff --git a/algorithms/two_pointers/product_of_array_except_self/images/solutions/product_of_array_except_self_solution_2.png b/algorithms/two_pointers/product_of_array_except_self/images/solutions/product_of_array_except_self_solution_2.png new file mode 100644 index 00000000..e3cb71d5 Binary files /dev/null and b/algorithms/two_pointers/product_of_array_except_self/images/solutions/product_of_array_except_self_solution_2.png differ diff --git a/algorithms/two_pointers/product_of_array_except_self/images/solutions/product_of_array_except_self_solution_3.png b/algorithms/two_pointers/product_of_array_except_self/images/solutions/product_of_array_except_self_solution_3.png new file mode 100644 index 00000000..63cc4b48 Binary files /dev/null and b/algorithms/two_pointers/product_of_array_except_self/images/solutions/product_of_array_except_self_solution_3.png differ diff --git a/algorithms/two_pointers/product_of_array_except_self/images/solutions/product_of_array_except_self_solution_4.png b/algorithms/two_pointers/product_of_array_except_self/images/solutions/product_of_array_except_self_solution_4.png new file mode 100644 index 00000000..e739ad03 Binary files /dev/null and b/algorithms/two_pointers/product_of_array_except_self/images/solutions/product_of_array_except_self_solution_4.png differ diff --git a/algorithms/two_pointers/product_of_array_except_self/images/solutions/product_of_array_except_self_solution_5.png b/algorithms/two_pointers/product_of_array_except_self/images/solutions/product_of_array_except_self_solution_5.png new file mode 100644 index 00000000..69ebc14f Binary files /dev/null and b/algorithms/two_pointers/product_of_array_except_self/images/solutions/product_of_array_except_self_solution_5.png differ diff --git a/algorithms/two_pointers/product_of_array_except_self/images/solutions/product_of_array_except_self_solution_6.png b/algorithms/two_pointers/product_of_array_except_self/images/solutions/product_of_array_except_self_solution_6.png new file mode 100644 index 00000000..b6d20267 Binary files /dev/null and b/algorithms/two_pointers/product_of_array_except_self/images/solutions/product_of_array_except_self_solution_6.png differ diff --git a/algorithms/two_pointers/product_of_array_except_self/images/solutions/product_of_array_except_self_solution_7.png b/algorithms/two_pointers/product_of_array_except_self/images/solutions/product_of_array_except_self_solution_7.png new file mode 100644 index 00000000..43d2d0d8 Binary files /dev/null and b/algorithms/two_pointers/product_of_array_except_self/images/solutions/product_of_array_except_self_solution_7.png differ diff --git a/algorithms/two_pointers/product_of_array_except_self/images/solutions/product_of_array_except_self_solution_8.png b/algorithms/two_pointers/product_of_array_except_self/images/solutions/product_of_array_except_self_solution_8.png new file mode 100644 index 00000000..7e60e20b Binary files /dev/null and b/algorithms/two_pointers/product_of_array_except_self/images/solutions/product_of_array_except_self_solution_8.png differ diff --git a/algorithms/two_pointers/product_of_array_except_self/images/solutions/product_of_array_except_self_solution_9.png b/algorithms/two_pointers/product_of_array_except_self/images/solutions/product_of_array_except_self_solution_9.png new file mode 100644 index 00000000..46fcae6f Binary files /dev/null and b/algorithms/two_pointers/product_of_array_except_self/images/solutions/product_of_array_except_self_solution_9.png differ diff --git a/algorithms/two_pointers/product_of_array_except_self/test_product_except_self.py b/algorithms/two_pointers/product_of_array_except_self/test_product_except_self.py new file mode 100644 index 00000000..cc21f782 --- /dev/null +++ b/algorithms/two_pointers/product_of_array_except_self/test_product_except_self.py @@ -0,0 +1,37 @@ +import unittest +from typing import List +from parameterized import parameterized +from algorithms.two_pointers.product_of_array_except_self import ( + product_except_self_prefix_sums, + product_except_self_two_pointers, +) + +PRODUCT_OF_ARRAY_EXCEPT_SELF_TEST_CASES = [ + ([1, 2, 3, 4], [24, 12, 8, 6]), + ([-1, 1, 0, -3, 3], [0, 0, 9, 0, 0]), + ([1, -3, 5, 7, -11], [1155, -385, 231, 165, -105]), + ([2, 4, 0, 6], [0, 0, 48, 0]), + ([0, -1, 2, -3, 4, -2], [-48, 0, 0, 0, 0, 0]), + ([5, 3, -1, 6, 4], [-72, -120, 360, -60, -90]), + ([-7, 6, 4, 3, 1, 2], [144, -168, -252, -336, -1008, -504]), +] + + +class ProductOfArrayExceptSelfTestCases(unittest.TestCase): + @parameterized.expand(PRODUCT_OF_ARRAY_EXCEPT_SELF_TEST_CASES) + def test_product_of_array_except_self_prefix_sums( + self, nums: List[int], expected: List[int] + ): + actual = product_except_self_prefix_sums(nums) + self.assertEqual(expected, actual) + + @parameterized.expand(PRODUCT_OF_ARRAY_EXCEPT_SELF_TEST_CASES) + def test_product_of_array_except_self_two_pointers( + self, nums: List[int], expected: List[int] + ): + actual = product_except_self_two_pointers(nums) + self.assertEqual(expected, actual) + + +if __name__ == "__main__": + unittest.main() diff --git a/puzzles/arrays/product_of_array_except_self/README.md b/puzzles/arrays/product_of_array_except_self/README.md deleted file mode 100644 index c92a0684..00000000 --- a/puzzles/arrays/product_of_array_except_self/README.md +++ /dev/null @@ -1,24 +0,0 @@ -# Product of Array Except Self - -Given an integer array nums, return an array answer such that answer[i] is equal to the product of all the elements of -nums except nums[i]. - -The product of any prefix or suffix of nums is guaranteed to fit in a 32-bit integer. - -You must write an algorithm that runs in O(n) time and without using the division operation. - -```plain -Example 1: - -Input: nums = [1,2,3,4] -Output: [24,12,8,6] -Example 2: - -Input: nums = [-1,1,0,-3,3] -Output: [0,0,9,0,0] -``` - -## Related Topics - -- Array -- Prefix Sum diff --git a/puzzles/arrays/product_of_array_except_self/__init__.py b/puzzles/arrays/product_of_array_except_self/__init__.py deleted file mode 100644 index 29efec2a..00000000 --- a/puzzles/arrays/product_of_array_except_self/__init__.py +++ /dev/null @@ -1,20 +0,0 @@ -from typing import List - - -def product_except_self(nums: List[int]) -> List[int]: - if len(nums) <= 1: - return nums - - result = [1] * len(nums) - prefix = 1 - - for i in range(len(nums)): - result[i] = prefix - prefix *= nums[i] - - postfix = 1 - for i in range(len(nums) - 1, -1, -1): - result[i] *= postfix - postfix *= nums[i] - - return result diff --git a/puzzles/arrays/product_of_array_except_self/test_product_except_self.py b/puzzles/arrays/product_of_array_except_self/test_product_except_self.py deleted file mode 100644 index 12406d28..00000000 --- a/puzzles/arrays/product_of_array_except_self/test_product_except_self.py +++ /dev/null @@ -1,25 +0,0 @@ -import unittest - -from . import product_except_self - - -class ProductOfArrayExceptSelfTestCases(unittest.TestCase): - def test_1_2_3_4_returns_24_12_8_6(self): - """Should return [24,12,8, 6] from input of [1,2,3,4]""" - nums = [1, 2, 3, 4] - expected = [24, 12, 8, 6] - actual = product_except_self(nums) - - self.assertEqual(expected, actual) - - def test_1_1_0_3_3_returns_0_0_9_0_0(self): - """Should return [0,0,9,0,0] from input of [-1,1,0,-3,3]""" - nums = [-1, 1, 0, -3, 3] - expected = [0, 0, 9, 0, 0] - actual = product_except_self(nums) - - self.assertEqual(expected, actual) - - -if __name__ == "__main__": - unittest.main()