-
Notifications
You must be signed in to change notification settings - Fork 2
feat(algorithms, two-pointers): product of array except self #176
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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 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. | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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 | ||
|
Comment on lines
+4
to
+6
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Inconsistent single-element behavior between the two implementations.
Consider either removing this early return or changing it to Proposed fix def product_except_self_prefix_sums(nums: List[int]) -> List[int]:
- if len(nums) <= 1:
- return nums
-
result = [1] * len(nums)
prefix = 1🤖 Prompt for AI Agents |
||
|
|
||
| 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 | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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() |
This file was deleted.
This file was deleted.
This file was deleted.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Stray empty bullet point.
Line 103 has a lone
-that appears to be a leftover. Remove it.Proposed fix
- Move the pointers toward each other to process the entire list. --📝 Committable suggestion
🤖 Prompt for AI Agents