LeetCode 1822 - Sign of the Product of an Array
The problem asks us to determine the sign of the product of all numbers in an integer array, without necessarily computing the actual product itself.
Difficulty: 🟢 Easy
Topics: Array, Math
Solution
Problem Understanding
The problem asks us to determine the sign of the product of all numbers in an integer array, without necessarily computing the actual product itself.
A helper function signFunc(x) is defined as:
- Return
1ifx > 0 - Return
-1ifx < 0 - Return
0ifx == 0
We are given an array nums, and we conceptually multiply all its elements together to form a value called product. Our task is to return the sign of that product.
For example:
- If the array contains an even number of negative values and no zeroes, the final product is positive, so the answer is
1. - If the array contains an odd number of negative values and no zeroes, the final product is negative, so the answer is
-1. - If any value is
0, the entire product becomes0, so the answer is0.
The constraints are small:
1 <= nums.length <= 1000-100 <= nums[i] <= 100
Although the values themselves are not large, repeatedly multiplying numbers together could still create very large products in other languages or scenarios. More importantly, the problem only asks for the sign, not the actual product value. This observation leads directly to a more efficient and cleaner solution.
Some important edge cases include:
- Arrays containing a zero, because the answer immediately becomes
0 - Arrays with exactly one negative number
- Arrays with many negative numbers, where parity matters
- Arrays of length
1 - Arrays containing only positive values
The problem guarantees that the array is non-empty, so we never need to handle an empty input.
Approaches
Brute Force Approach
The most straightforward approach is to compute the actual product of all numbers in the array and then determine its sign at the end.
We initialize a variable product = 1 and multiply every element into it. After processing the array:
- If
product > 0, return1 - If
product < 0, return-1 - Otherwise return
0
This works because multiplication preserves the correct mathematical result.
However, this approach is not ideal. Even though the constraints in this problem are small enough to avoid overflow in Python, other languages may overflow when multiplying many integers together. More importantly, computing the entire product is unnecessary because only the sign matters.
Optimal Approach
The key observation is that we never need the actual product value.
The sign of a product depends only on two things:
- Whether any number is zero
- Whether the count of negative numbers is odd or even
The rules are:
- If any element is
0, the answer is immediately0 - Every negative number flips the sign
- An even number of negative values produces a positive result
- An odd number of negative values produces a negative result
Instead of computing the product, we can simply track the current sign.
We initialize sign = 1.
For each number:
- If the number is
0, return0 - If the number is negative, multiply
signby-1 - Otherwise do nothing
At the end, return sign.
This avoids unnecessary multiplication and keeps the logic extremely simple.
| Approach | Time Complexity | Space Complexity | Notes |
|---|---|---|---|
| Brute Force | O(n) | O(1) | Computes the full product directly |
| Optimal | O(n) | O(1) | Tracks only the sign using negative count parity |
Algorithm Walkthrough
- Initialize a variable
sign = 1.
This variable represents the sign of the product seen so far. We start with positive because multiplying by 1 does not change anything.
2. Iterate through every number in the array.
We process each element one at a time to determine how it affects the final sign.
3. If the current number is 0, immediately return 0.
Any product containing zero becomes zero, so we can stop early without processing the remaining elements. 4. If the current number is negative, flip the sign.
A negative number changes a positive product into a negative one, or vice versa. We can implement this by multiplying sign by -1.
5. If the current number is positive, do nothing.
Positive numbers do not change the sign of the product.
6. After processing all elements, return sign.
At this point, sign correctly represents whether the total product is positive or negative.
Why it works
The algorithm works because the sign of multiplication follows simple mathematical rules:
- Multiplying by a positive number preserves the current sign
- Multiplying by a negative number flips the sign
- Multiplying by zero makes the entire product zero
By tracking only these sign changes, we obtain exactly the same result as computing the full product, while avoiding unnecessary arithmetic.
Python Solution
from typing import List
class Solution:
def arraySign(self, nums: List[int]) -> int:
sign = 1
for num in nums:
if num == 0:
return 0
if num < 0:
sign *= -1
return sign
The implementation begins by initializing sign to 1, representing a positive product.
We then iterate through each value in nums.
If a value is 0, we immediately return 0 because the final product must be zero regardless of the remaining elements.
If a value is negative, we multiply sign by -1. This flips the sign each time we encounter a negative number.
Positive numbers are ignored because they do not affect the sign.
After the loop finishes, sign contains the correct answer and is returned.
Go Solution
func arraySign(nums []int) int {
sign := 1
for _, num := range nums {
if num == 0 {
return 0
}
if num < 0 {
sign *= -1
}
}
return sign
}
The Go implementation follows the exact same logic as the Python version.
One notable difference is that Go uses a range loop to iterate through the slice. Since we never compute the actual product, integer overflow is not a concern.
The problem guarantees that the input slice contains at least one element, so no special handling for empty slices is necessary.
Worked Examples
Example 1
Input:
nums = [-1,-2,-3,-4,3,2,1]
Initial state:
sign = 1
| Current Number | Action | sign |
|---|---|---|
| -1 | Negative, flip sign | -1 |
| -2 | Negative, flip sign | 1 |
| -3 | Negative, flip sign | -1 |
| -4 | Negative, flip sign | 1 |
| 3 | Positive, no change | 1 |
| 2 | Positive, no change | 1 |
| 1 | Positive, no change | 1 |
Final answer:
1
Example 2
Input:
nums = [1,5,0,2,-3]
Initial state:
sign = 1
| Current Number | Action | sign |
|---|---|---|
| 1 | Positive, no change | 1 |
| 5 | Positive, no change | 1 |
| 0 | Return immediately | 0 |
Final answer:
0
Example 3
Input:
nums = [-1,1,-1,1,-1]
Initial state:
sign = 1
| Current Number | Action | sign |
|---|---|---|
| -1 | Negative, flip sign | -1 |
| 1 | Positive, no change | -1 |
| -1 | Negative, flip sign | 1 |
| 1 | Positive, no change | 1 |
| -1 | Negative, flip sign | -1 |
Final answer:
-1
Complexity Analysis
| Measure | Complexity | Explanation |
|---|---|---|
| Time | O(n) | We scan the array once |
| Space | O(1) | Only a single variable is used |
The algorithm performs one pass through the array, so the runtime grows linearly with the number of elements.
The memory usage remains constant because we only store the sign variable and do not allocate any extra data structures.
Test Cases
from typing import List
class Solution:
def arraySign(self, nums: List[int]) -> int:
sign = 1
for num in nums:
if num == 0:
return 0
if num < 0:
sign *= -1
return sign
sol = Solution()
assert sol.arraySign([-1, -2, -3, -4, 3, 2, 1]) == 1 # even negatives
assert sol.arraySign([1, 5, 0, 2, -3]) == 0 # contains zero
assert sol.arraySign([-1, 1, -1, 1, -1]) == -1 # odd negatives
assert sol.arraySign([5]) == 1 # single positive
assert sol.arraySign([-5]) == -1 # single negative
assert sol.arraySign([0]) == 0 # single zero
assert sol.arraySign([1, 2, 3, 4]) == 1 # all positive
assert sol.arraySign([-1, -2, -3, -4]) == 1 # even count negatives
assert sol.arraySign([-1, -2, -3]) == -1 # odd count negatives
assert sol.arraySign([100, -100, 100, -100]) == 1 # alternating signs
assert sol.arraySign([1, 2, 3, 0, 4, 5]) == 0 # zero in middle
assert sol.arraySign([-1] * 999) == -1 # large odd negative count
assert sol.arraySign([-1] * 1000) == 1 # large even negative count
| Test | Why |
|---|---|
[-1, -2, -3, -4, 3, 2, 1] |
Validates even number of negatives |
[1, 5, 0, 2, -3] |
Validates early zero detection |
[-1, 1, -1, 1, -1] |
Validates odd negatives |
[5] |
Smallest positive input |
[-5] |
Smallest negative input |
[0] |
Single zero case |
[1, 2, 3, 4] |
All positive numbers |
[-1, -2, -3, -4] |
Even negative count gives positive |
[-1, -2, -3] |
Odd negative count gives negative |
[100, -100, 100, -100] |
Mixed positive and negative values |
[1, 2, 3, 0, 4, 5] |
Zero appearing mid-array |
[-1] * 999 |
Large odd count stress test |
[-1] * 1000 |
Large even count stress test |
Edge Cases
A very important edge case is when the array contains a zero. A naive implementation that continues multiplying values unnecessarily may still work, but recognizing zero early allows the algorithm to terminate immediately. In this implementation, the moment a 0 is encountered, the function returns 0.
Another important case is an odd number of negative values. Since each negative flips the sign, forgetting to track parity correctly can produce the wrong result. The implementation handles this cleanly by multiplying the sign variable by -1 every time a negative value appears.
Arrays of length one are also important. The array may contain a single positive number, a single negative number, or zero. Since the algorithm processes each element independently and directly applies the sign rules, these smallest possible inputs are handled naturally without any special-case logic.