2357. Make Array Zero by Subtracting Equal Amounts
Problem Description
You have an array nums
containing non-negative integers. You need to reduce all elements to 0 using a specific type of operation.
In each operation, you must:
- Pick a positive integer
x
wherex
cannot be larger than the smallest non-zero element currently innums
- Subtract
x
from every positive element innums
(elements that are 0 remain unchanged)
Your goal is to find the minimum number of operations needed to make all elements in nums
equal to 0.
The key insight is that when you perform an operation, you can choose x
to be exactly equal to the smallest non-zero element. This will reduce that smallest element (and all other elements with the same value) to 0 in one operation. Elements larger than x
will be reduced but remain positive.
Since we can eliminate all occurrences of the smallest non-zero value in each operation, the minimum number of operations equals the number of distinct non-zero values in the array. This is why the solution simply counts unique non-zero elements using len({x for x in nums if x})
- it creates a set of all non-zero values and returns its size.
For example, if nums = [1, 5, 0, 3, 5]
:
- Distinct non-zero values are {1, 3, 5}
- We need 3 operations:
- Operation 1: Choose
x = 1
, array becomes[0, 4, 0, 2, 4]
- Operation 2: Choose
x = 2
, array becomes[0, 2, 0, 0, 2]
- Operation 3: Choose
x = 2
, array becomes[0, 0, 0, 0, 0]
- Operation 1: Choose
Intuition
Let's think about what happens during each operation. When we choose a value x
and subtract it from all positive elements, we're essentially "shifting down" all positive values by the same amount.
The crucial observation is that if we always choose x
to be the smallest non-zero element in the array, we accomplish something important: we eliminate that value entirely from the array (along with all duplicates of that value). Why? Because subtracting the smallest non-zero value from itself gives 0, and subtracting it from any duplicate also gives 0.
Consider what happens step by step:
- In the first operation, we eliminate the smallest non-zero value
- In the second operation, we eliminate what becomes the new smallest non-zero value
- This continues until all values reach 0
Since we can eliminate one unique value per operation (along with all its duplicates), and we need to eliminate all unique non-zero values to make the entire array 0, the minimum number of operations is exactly equal to the count of distinct non-zero values.
This transforms our problem from "how many operations do we need?" to simply "how many different non-zero values exist in the array?". The duplicates don't matter because they get eliminated together in the same operation.
That's why the solution is elegantly simple: len({x for x in nums if x})
. We create a set of all non-zero elements (which automatically removes duplicates) and count its size. This count directly gives us the minimum number of operations needed.
Learn more about Greedy, Sorting and Heap (Priority Queue) patterns.
Solution Approach
The solution uses a hash table (implemented as a Python set) to count the number of distinct non-zero elements in the array. This approach is both elegant and efficient.
Implementation Steps:
-
Create a set of non-zero elements: The expression
{x for x in nums if x}
creates a set comprehension that:- Iterates through each element
x
innums
- Filters out zeros using the condition
if x
(since 0 evaluates toFalse
in Python) - Automatically removes duplicates (property of sets)
- Iterates through each element
-
Count the distinct elements: The
len()
function returns the size of the set, which represents the number of unique non-zero values.
Why this works:
- Each operation can eliminate all occurrences of one unique value from the array
- We need exactly as many operations as there are unique non-zero values
- The set data structure naturally handles the uniqueness requirement
Time and Space Complexity:
- Time Complexity:
O(n)
wheren
is the length of the array, as we iterate through the array once - Space Complexity:
O(k)
wherek
is the number of distinct non-zero elements in the array (at mostn
)
Alternative Implementation: You could also use an array or explicit hash table to count distinct elements, but the set approach is the most concise and Pythonic way to solve this problem.
Example walkthrough with nums = [1, 5, 0, 3, 5]
:
- Set comprehension creates:
{1, 3, 5}
(the 0 is filtered out, duplicate 5 is removed) len({1, 3, 5})
returns3
- Therefore, minimum operations needed =
3
Ready to land your dream job?
Unlock your dream job with a 5-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through a detailed example with nums = [2, 2, 3, 1, 1, 0]
.
Step 1: Identify distinct non-zero values
- Looking at our array:
[2, 2, 3, 1, 1, 0]
- Non-zero elements:
2, 2, 3, 1, 1
- Distinct non-zero values:
{1, 2, 3}
- Count = 3, so we'll need 3 operations
Step 2: Simulate the operations
Operation 1:
- Current array:
[2, 2, 3, 1, 1, 0]
- Smallest non-zero element:
1
- Choose
x = 1
- Subtract 1 from all positive elements:
2 - 1 = 1
2 - 1 = 1
3 - 1 = 2
1 - 1 = 0
1 - 1 = 0
0
stays0
- Result:
[1, 1, 2, 0, 0, 0]
Operation 2:
- Current array:
[1, 1, 2, 0, 0, 0]
- Smallest non-zero element:
1
- Choose
x = 1
- Subtract 1 from all positive elements:
1 - 1 = 0
1 - 1 = 0
2 - 1 = 1
- Zeros remain unchanged
- Result:
[0, 0, 1, 0, 0, 0]
Operation 3:
- Current array:
[0, 0, 1, 0, 0, 0]
- Smallest non-zero element:
1
- Choose
x = 1
- Subtract 1 from all positive elements:
- Only the
1
becomes0
- All other elements are already 0
- Only the
- Result:
[0, 0, 0, 0, 0, 0]
Step 3: Verify with our solution
- Using the set comprehension:
{x for x in nums if x}
- This creates:
{2, 3, 1}
(zeros filtered out, duplicates removed) len({2, 3, 1})
= 3- Matches our simulation: 3 operations needed ✓
The key insight: We eliminated exactly one distinct value per operation (first all 1s, then the remaining 1s that came from 2s, then the final 1 that came from 3). This confirms that counting distinct non-zero values gives us the minimum operations needed.
Solution Implementation
1class Solution:
2 def minimumOperations(self, nums: List[int]) -> int:
3 # Create a set of unique non-zero elements from the input array
4 # The set comprehension filters out zeros (x for x in nums if x)
5 # since 'if x' evaluates to False when x is 0
6 unique_non_zero_elements = {x for x in nums if x}
7
8 # The minimum number of operations needed is equal to
9 # the count of unique non-zero values in the array
10 # Each operation reduces all instances of the smallest positive value to 0
11 return len(unique_non_zero_elements)
12
1class Solution {
2 public int minimumOperations(int[] nums) {
3 // Boolean array to track which values have been encountered
4 // Index represents the value (0-100), true means the value has been seen
5 boolean[] seen = new boolean[101];
6
7 // Mark 0 as already seen (we don't count zeros as distinct values)
8 seen[0] = true;
9
10 // Counter for distinct non-zero values
11 int distinctCount = 0;
12
13 // Iterate through all numbers in the input array
14 for (int num : nums) {
15 // If this value hasn't been seen before
16 if (!seen[num]) {
17 // Increment the count of distinct values
18 distinctCount++;
19 // Mark this value as seen
20 seen[num] = true;
21 }
22 }
23
24 // Return the total count of distinct non-zero values
25 return distinctCount;
26 }
27}
28
1class Solution {
2public:
3 int minimumOperations(vector<int>& nums) {
4 // Boolean array to track which values have been seen
5 // Index represents the value (0-100), value represents if it's been seen
6 bool seen[101]{};
7
8 // Mark 0 as already seen (since we don't need to operate on zeros)
9 seen[0] = true;
10
11 // Counter for minimum operations needed
12 int operationCount = 0;
13
14 // Iterate through each element in the input array
15 for (int& num : nums) {
16 // If this value hasn't been seen before
17 if (!seen[num]) {
18 // Increment the operation count (we need one operation for each unique non-zero value)
19 ++operationCount;
20 // Mark this value as seen
21 seen[num] = true;
22 }
23 }
24
25 // Return the total number of operations needed
26 return operationCount;
27 }
28};
29
1/**
2 * Calculates the minimum number of operations needed to make all elements zero.
3 * Each operation allows selecting a positive element and subtracting it from all elements.
4 *
5 * The solution leverages the fact that the minimum operations equals the number of unique positive values.
6 * Each unique positive value requires exactly one operation to reduce to zero.
7 *
8 * @param nums - Array of non-negative integers
9 * @returns The minimum number of operations required
10 */
11function minimumOperations(nums: number[]): number {
12 // Create a set to store unique values from the array
13 const uniqueValues: Set<number> = new Set(nums);
14
15 // Remove zero from the set since zeros don't require any operations
16 uniqueValues.delete(0);
17
18 // The number of remaining unique positive values is the minimum operations needed
19 return uniqueValues.size;
20}
21
Time and Space Complexity
The time complexity is O(n)
, where n
is the length of the array nums
. This is because the code iterates through all elements in nums
once to create a set comprehension, checking each element with the condition if x
(which filters out zero values).
The space complexity is O(n)
in the worst case, where n
is the length of the array nums
. This occurs when all elements in nums
are non-zero and unique, resulting in a set that contains up to n
elements. The set comprehension {x for x in nums if x}
creates a new set to store the unique non-zero values from the input array.
Learn more about how to find time and space complexity quickly.
Common Pitfalls
Pitfall 1: Trying to Simulate the Operations
Many people initially attempt to simulate the actual operations by repeatedly finding the minimum non-zero element and subtracting it from all positive elements:
Incorrect Approach:
def minimumOperations(self, nums: List[int]) -> int:
operations = 0
while any(nums): # While there are non-zero elements
min_val = min(x for x in nums if x > 0)
nums = [x - min_val if x > 0 else 0 for x in nums]
operations += 1
return operations
Issues:
- Time complexity becomes O(n²) or worse
- Unnecessarily complex and inefficient
- Modifies the array multiple times
Solution: Recognize that you don't need to perform the actual operations. The answer depends only on counting distinct non-zero values.
Pitfall 2: Forgetting to Filter Out Zeros
A common mistake is counting all distinct values including zero:
Incorrect Code:
def minimumOperations(self, nums: List[int]) -> int:
return len(set(nums)) # Wrong! This counts 0 as a distinct value
Why it's wrong: If nums = [1, 0, 3]
, this returns 3 instead of 2, because it counts {0, 1, 3} instead of {1, 3}.
Solution: Always filter out zeros before counting: len({x for x in nums if x})
or len(set(nums) - {0})
Pitfall 3: Overthinking the Value of x
Some may think they need to optimize the choice of x
in each operation or consider different strategies for selecting x
.
Misconception: "Maybe choosing a different value of x (not the minimum) could reduce operations"
Clarification: The optimal strategy is always to choose x
equal to the smallest non-zero element. Any other choice would leave that smallest element positive, requiring an additional operation later.
Pitfall 4: Using Complex Data Structures
Some might use unnecessary data structures like Counter or sorted lists:
Overcomplicated:
from collections import Counter
def minimumOperations(self, nums: List[int]) -> int:
counter = Counter(nums)
if 0 in counter:
del counter[0]
return len(counter)
Solution: A simple set comprehension is sufficient and more elegant: {x for x in nums if x}
A heap is a ...?
Recommended Readings
Greedy Introduction div class responsive iframe iframe src https www youtube com embed WTslqPbj7I title YouTube video player frameborder 0 allow accelerometer autoplay clipboard write encrypted media gyroscope picture in picture web share allowfullscreen iframe div When do we use greedy Greedy algorithms tend to solve optimization problems Typically they will ask you to calculate the max min of some value Commonly you may see this phrased in the problem as max min longest shortest largest smallest etc These keywords can be identified by just scanning
Sorting Summary Comparisons We presented quite a few sorting algorithms and it is essential to know the advantages and disadvantages of each one The basic algorithms are easy to visualize and easy to learn for beginner programmers because of their simplicity As such they will suffice if you don't know any advanced
https assets algo monster cover_photos heap svg Priority Queue and Heap What is the relationship between priority queue and heap Priority Queue is an Abstract Data Type and Heap is the concrete data structure we use to implement a priority queue Priority Queue A priority queue is a data structure
Want a Structured Path to Master System Design Too? Don’t Miss This!