Apply Operations to Make All Array Elements Equal to Zero
MedPrompt
You are given a 0-indexed integer array nums and a positive integer k.
You can apply the following operation on the array any number of times:
- Choose any subarray of size
kfrom the array and decrease all its elements by1.
Return true if you can make all the array elements equal to 0, or false otherwise.
A subarray is a contiguous non-empty part of an array.
Example 1:
Input: nums = [2,2,3,1,1,0], k = 3
Output: true
Explanation: We can do the following operations:
- Choose the subarray [2,2,3]. The resulting array will be nums = [1,1,2,1,1,0].
- Choose the subarray [2,1,1]. The resulting array will be nums = [1,1,1,0,0,0].
- Choose the subarray [1,1,1]. The resulting array will be nums = [0,0,0,0,0,0].Example 2:
Input: nums = [1,3,1,1], k = 2
Output: false
Explanation: It is not possible to make all the array elements equal to 0.
Constraints:
1 <= k <= nums.length <= 1050 <= nums[i] <= 106
Approaches
2 approaches with complexity analysis and trade-offs.
This approach directly simulates the process based on a greedy strategy. We iterate through the array from left to right. For each element nums[i], we must make it zero. Since we have already processed elements nums[0...i-1], we cannot apply any operation that affects them. The only way to reduce nums[i] without changing previous elements is by applying operations on subarrays starting at index i. Therefore, we must apply the decrement operation exactly nums[i] times on the subarray nums[i...i+k-1]. We repeat this for every element in the array.
Algorithm
- Iterate through the array
numswith an indexifrom0ton-1, wherenis the length ofnums. - If
nums[i]is negative at any point, it's an impossible state. Returnfalse. - If
nums[i]is0, no operations are needed for this element, so continue to the next index. - If
nums[i]is positive, this value represents the number of decrement operations we must apply to a subarray of sizekstarting ati. - Check if a subarray of size
kcan be formed starting ati. Ifi + k > n, we cannot perform the operation, and sincenums[i]is positive, it's impossible to make it zero. Returnfalse. - If a valid subarray can be formed, subtract
nums[i]from all elements in the subarraynums[i...i+k-1]. - After the loop finishes, all elements from
nums[0]tonums[n-2]have been processed and effectively turned to zero. The possibility of success hinges on the final value ofnums[n-1]. - Return
trueifnums[n-1]is0, andfalseotherwise.
Walkthrough
The algorithm iterates through the array nums from left to right. At each index i, it ensures the element nums[i] becomes zero. If nums[i] is already zero, we move on. If nums[i] is positive, we must perform nums[i] decrement operations. To avoid altering the already-zeroed elements before i, these operations must apply to a subarray starting at i. This is only possible if a subarray of size k fits within the array bounds (i.e., i + k <= n). If nums[i] is positive but such an operation is not possible, we cannot make it zero, so we return false. Otherwise, we subtract nums[i] from all elements in the subarray nums[i...i+k-1]. If at any point an element becomes negative, it's an impossible scenario, and we return false. After iterating through the entire array, if we have successfully made nums[0...n-2] zero, the final answer depends on whether nums[n-1] has also become zero.
class Solution { public boolean checkArray(int[] nums, int k) { // With k=1, we can decrement any element individually. // Since all nums[i] >= 0, we can always make them 0. if (k == 1) { return true; } int n = nums.length; for (int i = 0; i < n; i++) { if (nums[i] < 0) { return false; } if (nums[i] == 0) { continue; } // If nums[i] > 0, we must perform operations. // This is impossible if the subarray goes out of bounds. if (i + k > n) { return false; } int ops = nums[i]; for (int j = i; j < i + k; j++) { nums[j] -= ops; } } // If we successfully processed all elements, the last one must be 0. return nums[n - 1] == 0; }}Complexity
Time
O(n * k), where n is the length of `nums`. The outer loop runs `n` times, and for each positive element, the inner loop runs `k` times.
Space
O(1) as we modify the array in-place and use a constant amount of extra space.
Trade-offs
Pros
Simple to understand and implement.
It's a direct translation of the greedy logic.
Cons
Inefficient for large
nandkdue to its O(n*k) time complexity, which will time out on larger constraints.
The naive simulation is slow because of the O(k) update step inside the main loop. We can optimize this by noticing that the decrements form a sliding window. Instead of applying k subtractions at each step, we can maintain a running sum of the operations that affect the current element. This sum, let's call it current_effect, can be updated in O(1) time at each step. As we move from index i to i+1, we add the effect of the new operations started at i and subtract the effect of the operations that just fell off the window's left edge (i.e., those started at i-k+1). This transforms the O(n*k) algorithm into a much faster O(n) one.
Algorithm
- Handle the edge case where
k=1. In this case, any non-negative array can be made zero, so returntrue. - Initialize a variable
current_effectto0. This will track the cumulative effect of operations on the current element. - Iterate through the array
numswith an indexifrom0ton-1. - If
current_effectis greater thannums[i], it means the element would become negative, which is not allowed. Returnfalse. - Calculate the number of operations that must start at index
i:ops_i = nums[i] - current_effect. - Update
current_effectby addingops_i, as its effect will carry over to the next elements in the window. - Reuse the
nums[i]entry to storeops_i. - If
i >= k - 1, an operation window is ending. Subtract the effect of the operation that started ati - k + 1(which is stored innums[i - k + 1]) fromcurrent_effect. - After the loop, the
current_effectmust be zero. This ensures that no operations are 'leaking' past the end of the array, which implies the last element was correctly zeroed out. Returntrueifcurrent_effect == 0,falseotherwise.
Walkthrough
This optimized approach still follows the same greedy strategy but improves the performance by avoiding the inner loop. We iterate through the array once, maintaining a variable current_effect that represents the total decrement applied to the current element nums[i] from operations started in the range [i-k+1, i-1].
At each index i:
- We check if
current_effectexceedsnums[i]. If it does, the element would become negative, which is impossible, so we returnfalse. - The number of operations we must start at
iisops_i = nums[i] - current_effect. - We add this
ops_itocurrent_effectbecause it will affect the subsequentk-1elements. - To keep track of
ops_ifor later removal from the sliding window, we can cleverly overwritenums[i]with this value. - When our index
iisk-1or greater, it means the window has moved one step, and the operation that started ati-k+1is no longer in effect. We subtract its value (which we stored innums[i-k+1]) fromcurrent_effect.
After the loop, if current_effect is zero, it means all operations were perfectly contained within the array and the last element was successfully zeroed out. If current_effect is non-zero, it implies that some operations would need to extend beyond the array's bounds, which is not possible.
class Solution { public boolean checkArray(int[] nums, int k) { if (k == 1) { return true; } int n = nums.length; // current_effect represents the sum of operations in the current window. long current_effect = 0; for (int i = 0; i < n; i++) { // If the total decrement effect is greater than the current element, // it means the element would become negative. Impossible. if (current_effect > nums[i]) { return false; } // The number of operations to start at index i is the remaining value. // We reuse nums[i] to store this count. long ops_i = nums[i] - current_effect; nums[i] = (int)ops_i; // Add the effect of the new operations. current_effect += ops_i; // If the window has moved past the k-th element, // remove the effect of the operation that is now out of the window. if (i >= k - 1) { current_effect -= nums[i - k + 1]; } } // At the end, the current_effect must be zero. This implies that the // operations perfectly terminated at the end of the array, leaving // the final effective element nums[n-1] as zero. return current_effect == 0; }}Complexity
Time
O(n), where n is the length of `nums`. We iterate through the array only once.
Space
O(1), as we only use a few extra variables and modify the input array.
Trade-offs
Pros
Highly efficient with linear time complexity.
Space-efficient as it modifies the array in-place.
Cons
The logic is more complex than the naive approach, involving a sliding window sum concept.
Modifies the input array, which might not be desirable in some contexts (though acceptable for this problem).
Solutions
Solution
class Solution {public boolean checkArray(int[] nums, int k) { int n = nums.length; int[] d = new int[n + 1]; int s = 0; for (int i = 0; i < n; ++i) { s += d[i]; nums[i] += s; if (nums[i] == 0) { continue; } if (nums[i] < 0 || i + k > n) { return false; } s -= nums[i]; d[i + k] += nums[i]; } return true; }}class Solution {public: bool checkArray(vector<int> &nums, int k) { int n = nums.size(); vector<int> d(n + 1); int s = 0; for (int i = 0; i < n; ++i) { s += d[i]; nums[i] += s; if (nums[i] == 0) { continue; } if (nums[i] < 0 || i + k > n) { return false; } s -= nums[i]; d[i + k] += nums[i]; } return true; }};class Solution: def checkArray(self, nums: List[int], k: int) -> bool: n = len(nums) d = [0] * (n + 1) s = 0 for i, x in enumerate(nums): s += d[i] x += s if x == 0: continue if x < 0 or i + k > n: return False s -= x d[i + k] += x return TrueVideo walkthrough
Newsletter
One sharp idea, every week
System design and interview prep — short enough to finish.
No spam. Unsubscribe anytime.
Practice
Same difficulty — related problems to reinforce the pattern.