Medium
题目描述
给你一个下标从 0 开始、大小为 n 且由 正整数 组成的数组 nums 。
同时给你一个大小为 m 的二维数组 queries ,其中 queries[i] = [indexi, ki] 。
一开始,数组中的所有元素都 未标记 。
你需要依次对数组执行 m 次查询,其中第 i 次查询中你需要:
- 如果下标
indexi对应的元素还没有被标记,那么标记这个元素。 - 然后标记
ki个数组中还没有标记的 最小元素 。如果有多个这样的元素,优先标记下标 较小 的。如果少于ki个未标记元素,则标记所有剩下的未标记元素。
请你返回一个长度为 m 的数组 answer ,其中 answer[i] 是第 i 次查询后数组中所有 未标记元素 的和。
示例 1:
输入: nums = [1,2,2,1,2,3,1], queries = [[1,2],[3,3],[4,2]] 输出: [8,3,0] 解释: 我们依次对数组执行以下查询:
- 标记下标 1 的元素,然后标记 2 个最小的未标记元素。标记后的数组为 nums = [1,2,2,1,2,3,1] 。未标记元素的和为 1 + 2 + 3 = 6 。
- 标记下标 3 的元素,由于它已经被标记了,所以忽略。然后标记 3 个最小的未标记元素。标记后的数组为 nums = [1,2,2,1,2,3,1] 。未标记元素的和为 3 。
- 标记下标 4 的元素,由于它已经被标记了,所以忽略。然后标记 2 个最小的未标记元素。标记后的数组为 nums = [1,2,2,1,2,3,1] 。未标记元素的和为 0 。
示例 2:
输入: nums = [1,4,2,3], queries = [[0,1]] 输出: [7] 解释: 我们执行一次查询:标记下标 0 的元素,然后标记最小的 1 个未标记元素。标记后的数组为 nums = [1,4,2,3] ,未标记元素的和为 4 + 3 = 7 。
提示:
n == nums.lengthm == queries.length1 <= m <= n <= 1051 <= nums[i] <= 105queries[i].length == 20 <= indexi, ki <= n - 1
解题思路
这道题的核心思路是维护数组的标记状态,并能快速找到最小的未标记元素。
算法思路:
预处理排序:创建一个索引数组,按照元素值进行排序,相同值时按索引排序。这样可以快速找到最小的未标记元素。
维护标记状态:使用布尔数组记录每个位置是否已被标记。
处理每个查询:
- 首先标记指定索引的元素(如果未标记)
- 然后从排序后的索引数组中顺序查找,标记k个最小的未标记元素
- 计算剩余未标记元素的和
优化求和:维护总和变量,每次标记元素时减去对应值,避免重复计算。
关键优化点:
- 使用排序后的索引数组,可以O(1)时间找到下一个最小未标记元素
- 维护当前搜索位置指针,避免重复扫描已标记的元素
- 预计算总和,通过减法得到未标记元素和
时间复杂度主要在排序步骤O(n log n),查询处理为O(m + n)。
代码实现
class Solution {
public:
vector<long long> unmarkedSumArray(vector<int>& nums, vector<vector<int>>& queries) {
int n = nums.size();
vector<int> indices(n);
for (int i = 0; i < n; i++) {
indices[i] = i;
}
// 按值排序,相同值按索引排序
sort(indices.begin(), indices.end(), [&](int i, int j) {
if (nums[i] != nums[j]) return nums[i] < nums[j];
return i < j;
});
vector<bool> marked(n, false);
long long totalSum = 0;
for (int x : nums) totalSum += x;
vector<long long> result;
int ptr = 0; // 指向下一个可能标记的元素
for (auto& query : queries) {
int index = query[0], k = query[1];
// 标记指定索引
if (!marked[index]) {
marked[index] = true;
totalSum -= nums[index];
}
// 标记k个最小的未标记元素
int count = 0;
while (count < k && ptr < n) {
int idx = indices[ptr];
if (!marked[idx]) {
marked[idx] = true;
totalSum -= nums[idx];
count++;
}
ptr++;
}
result.push_back(totalSum);
}
return result;
}
};
class Solution:
def unmarkedSumArray(self, nums: List[int], queries: List[List[int]]) -> List[int]:
n = len(nums)
# 创建索引数组并排序
indices = list(range(n))
indices.sort(key=lambda i: (nums[i], i))
marked = [False] * n
total_sum = sum(nums)
result = []
ptr = 0 # 指向下一个可能标记的元素
for index, k in queries:
# 标记指定索引
if not marked[index]:
marked[index] = True
total_sum -= nums[index]
# 标记k个最小的未标记元素
count = 0
while count < k and ptr < n:
idx = indices[ptr]
if not marked[idx]:
marked[idx] = True
total_sum -= nums[idx]
count += 1
ptr += 1
result.append(total_sum)
return result
public class Solution {
public long[] UnmarkedSumArray(int[] nums, int[][] queries) {
int n = nums.Length;
var indices = new int[n];
for (int i = 0; i < n; i++) {
indices[i] = i;
}
Array.Sort(indices, (i, j) => {
if (nums[i] != nums[j]) return nums[i].CompareTo(nums[j]);
return i.CompareTo(j);
});
bool[] marked = new bool[n];
long totalSum = 0;
foreach (int x in nums) totalSum += x;
long[] result = new long[queries.Length];
int ptr = 0;
for (int q = 0; q < queries.Length; q++) {
int index = queries[q][0], k = queries[q][1];
if (!marked[index]) {
marked[index] = true;
totalSum -= nums[index];
}
int count = 0;
while (count < k && ptr < n) {
int idx = indices[ptr];
if (!marked[idx]) {
marked[idx] = true;
totalSum -= nums[idx];
count++;
}
ptr++;
}
result[q] = totalSum;
}
return result;
}
}
var unmarkedSumArray = function(nums, queries) {
const n = nums.length;
const indices = Array.from({length: n}, (_, i) => i);
indices.sort((i, j) => {
if (nums[i] !== nums[j]) return nums[i] - nums[j];
return i - j;
});
const marked = new Array(n).fill(false);
let totalSum = nums.reduce((sum, x) => sum + x, 0);
const result = [];
let ptr = 0;
for (const [index, k] of queries) {
if (!marked[index]) {
marked[index] = true;
totalSum -= nums[index];
}
let count = 0;
while (count < k && ptr < n) {
const idx = indices[ptr];
if (!marked[idx]) {
marked[idx] = true;
totalSum -= nums[idx];
count++;
}
ptr++;
}
result.push(totalSum);
}
return result;
};
复杂度分析
| 复杂度类型 | 时间复杂度 | 空间复杂度 |
|---|---|---|
| 时间复杂度 | O(n log n + m + n) | O(n) |
| 空间复杂度 | O(n) | O(n) |
说明:
- 时间复杂度:O(n log n)用于排序索引数组,O(m + n)用于处理所有查询(每个元素最多被访问一次)
- 空间复杂度:O(n)用于存储索引数组、标记数组等辅助空间