Problem
You are given an integer array nums
of length n
and a 2D array queries
where queries[i] = [li, ri]
.
Each queries[i]
represents the following action on nums
:
- Decrement the value at each index in the range
[li, ri]
innums
by at most 1. - The amount by which the value is decremented can be chosen independently for each index.
A Zero Array is an array with all its elements equal to 0.
Return the maximum number of elements that can be removed from queries
, such that nums
can still be converted to a zero array using the remaining queries. If it is not possible to convert nums
to a zero array, return -1.
Examples
Example 1:
Input: nums = [2,0,2], queries = [[0,2],[0,2],[1,1]]
Output: 1
Explanation:
After removing `queries[2]`, `nums` can still be converted to a zero array.
* Using `queries[0]`, decrement `nums[0]` and `nums[2]` by 1 and `nums[1]` by 0.
* Using `queries[1]`, decrement `nums[0]` and `nums[2]` by 1 and `nums[1]` by 0.
Example 2:
Input: nums = [1,1,1,1], queries = [[1,3],[0,2],[1,3],[1,2]]
Output: 2
Explanation:
We can remove `queries[2]` and `queries[3]`.
Example 3:
Input: nums = [1,2,3,4], queries = [[0,3]]
Output: -1
Explanation:
`nums` cannot be converted to a zero array even after using all the queries.
Constraints:
1 <= nums.length <= 105
0 <= nums[i] <= 105
1 <= queries.length <= 105
queries[i].length == 2
0 <= li <= ri < nums.length
Similar Problem
Zero Array Transformation 1 Zero Array Transformation 2
Solution
Method 1 - Using Min heap
- Use a min-heap to dynamically track the range end points of active queries that cover each index
i
innums
. - For each index
i
, attempt to reducenums[i]
to0
:- Remove expired ranges and add queries starting at or before
i
to the heap. - Use the “shortest” valid range (based on the heap’s smallest end index) to incrementally reduce
nums[i]
.
- Remove expired ranges and add queries starting at or before
- If any index cannot be reduced to
0
using available ranges, return-1
. - At the end, the size of the heap gives the number of unused queries.
Code
Java
class Solution {
public int maxRemoval(int[] nums, int[][] queries) {
int n = nums.length;
int qLen = queries.length;
// Sort queries by their start indices (greedy to process in order of 'li')
Arrays.sort(queries, (a, b) -> Integer.compare(a[0], b[0]));
// Min-heap to track the end indices of active queries (negated for min-heap operation)
PriorityQueue<Integer> activeEnds = new PriorityQueue<>();
int[] endCount = new int[n + 1]; // Stores decrement counts at indices after the end of ranges
int currentDecrements = 0; // Tracks active decrements applicable on nums[i]
int queryIndex = 0; // To iterate through sorted queries
// Traverse through every index of nums
for (int i = 0; i < n; i++) {
// Remove expired decrements (tied to indices beyond i)
currentDecrements -= endCount[i];
// Add new ranges starting at or before i into the heap
while (queryIndex < qLen && queries[queryIndex][0] <= i) {
activeEnds.offer(-queries[queryIndex][1]); // Push end index as negative for min-heap
queryIndex++;
}
// Ensure we fulfill nums[i] with available queries
while (currentDecrements < nums[i]) {
// If no valid query can cover current index, transformation is impossible
if (activeEnds.isEmpty() || -activeEnds.peek() < i) {
return -1;
}
// Use the shortest range available in the heap
int rangeEnd = -activeEnds.poll();
endCount[rangeEnd + 1]++; // Deferred decrement subtraction beyond the range
currentDecrements++; // Increment active decrements
}
}
// At the end, the heap size represents the number of removable queries
return activeEnds.size();
}
}
Python
class Solution:
def maxRemoval(self, nums: List[int], queries: List[List[int]]) -> int:
n = len(nums)
q_len = len(queries)
# Sort queries by their start index
queries.sort(key=lambda x: x[0])
# Min-heap to store the end indices of active queries
active_ends = []
end_count = [0] * (n + 1) # Decrement offsets for range ends
current_decrement = 0
query_idx = 0
for i in range(n):
# Remove expired decrements
current_decrement -= end_count[i]
# Add all new queries starting at or before index `i` to the heap
while query_idx < q_len and queries[query_idx][0] <= i:
heapq.heappush(active_ends, -queries[query_idx][1])
query_idx += 1
# Handle nums[i], ensuring it becomes 0
while current_decrement < nums[i]:
# If no valid range can cover index `i`, it's impossible
if not active_ends or -active_ends[0] < i:
return -1
# Use the shortest valid range (smallest end index)
range_end = -heapq.heappop(active_ends)
end_count[range_end + 1] += 1
current_decrement += 1
# The remaining size of the heap indicates the removable queries
return len(active_ends)
Complexity
- ⏰ Time complexity:
O(q log q + n log q)
where isn
is number of indices andq
is the number of queries.- Sorting the queries costs
O(q log q)
. - Each heap operation is
O(log q)
, resulting in a total ofO(q log q + n log q)
forn
indices.
- Sorting the queries costs
- 🧺 Space complexity:
O(q)
for using heap.