Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions DIRECTORY.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand Down
114 changes: 114 additions & 0 deletions algorithms/two_pointers/product_of_array_except_self/README.md
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 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.
-
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

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

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
-
- Move the pointers toward each other to process the entire list.
🤖 Prompt for AI Agents
In `@algorithms/two_pointers/product_of_array_except_self/README.md` at line 103,
Remove the stray empty bullet line in the README under the "Product of Array
Except Self" section: delete the lone "-" on line 103 so the markdown has no
empty list item; ensure surrounding list spacing stays correct and run a quick
markdown lint to confirm no leftover list artifacts in README.md.


### 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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Inconsistent single-element behavior between the two implementations.

product_except_self_prefix_sums returns nums as-is for single-element input (e.g., [5][5]), while product_except_self_two_pointers returns [1] for the same input. The mathematically correct answer (product of zero elements) is [1]. While LeetCode constrains n >= 2, this guard makes the two functions disagree.

Consider either removing this early return or changing it to return [1] for consistency:

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
In `@algorithms/two_pointers/product_of_array_except_self/__init__.py` around
lines 4 - 6, The early-return in product_except_self_prefix_sums currently
returns the input list for single-element arrays causing inconsistency with
product_except_self_two_pointers; update the guard in
product_except_self_prefix_sums so that for len(nums) <= 1 it returns [1] (the
product of zero elements) instead of nums, ensuring both implementations produce
[1] for single-element input.


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
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
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()
24 changes: 0 additions & 24 deletions puzzles/arrays/product_of_array_except_self/README.md

This file was deleted.

20 changes: 0 additions & 20 deletions puzzles/arrays/product_of_array_except_self/__init__.py

This file was deleted.

This file was deleted.

Loading