Kimi LeetCode 3321. 计算子数组的 x-sum II Python3实现
LeetCode 3321. 计算子数组的 x-sum II — Python3 实现
题目概述
给定数组 `nums`,整数 `k` 和 `x`。需要计算每个长度为 `k` 的滑动窗口的 x-sum:
1. 统计窗口中每个元素的出现次数
2. 保留出现次数最多的前 `x` 个元素(出现次数相同则数值大的优先)
3. 计算保留元素的总和
返回长度为 `n - k + 1` 的结果数组。
约束:`1 <= n <= 10^5`,需要 `O(n log k)` 的解法。
---
核心思路:哈希表 + 两个有序集合(SortedList)
使用 滑动窗口 配合两个有序集合:
数据结构 作用
`l`(Top 集合) 存储当前窗口中出现次数最多的前 `x` 个元素
`r`(剩余集合) 存储其余元素
`cnt`(Counter) 记录窗口中每个元素的频次
`s` 维护 `l` 中所有元素的 `频次 × 数值` 之和
排序规则:按 `(频次, 数值)` 升序排列。这样 `l[0]` 是 `l` 中最小的(最应该被淘汰的),`r.pop()` 取到的是 `r` 中最大的(最应该晋升的)。
滑动窗口操作:
1. 右端元素进入窗口:先 `remove` 旧状态,更新 `cnt`,再 `add` 新状态
2. 平衡 `l` 和 `r`:确保 `len(l) == x`(或窗口中不同元素不足 `x` 个时取全部)
3. 记录当前窗口的 `x-sum`
4. 左端元素离开窗口:同样先 `remove` 旧状态,更新 `cnt`,再 `add` 新状态
---
Python3 代码
```python
from sortedcontainers import SortedList
from collections import Counter
from typing import List
class Solution:
def findXSum(self, nums: List[int], k: int, x: int) -> List[int]:
"""
计算每个长度为 k 的滑动窗口的 x-sum
核心思路:哈希表 + 两个有序集合(SortedList)
- l: 存储前 x 个高频元素(按频次升序,频次相同按数值升序)
- r: 存储其余元素
- s: 维护 l 中所有元素的 (频次 × 数值) 之和
"""
def add(v: int):
"""将元素 v 的当前状态 (cnt[v], v) 加入合适的集合"""
if cnt[v] == 0:
return
p = (cnt[v], v)
# 如果 l 非空且 v 的状态比 l 中最小的还要大,则加入 l
if l and p > l[0]:
nonlocal s
s += p[0] * p[1]
l.add(p)
else:
r.add(p)
def remove(v: int):
"""从集合中移除元素 v 的当前状态 (cnt[v], v)"""
if cnt[v] == 0:
return
p = (cnt[v], v)
if p in l:
nonlocal s
s -= p[0] * p[1]
l.remove(p)
else:
r.remove(p)
# l: 前x个高频元素的集合(按频次升序,频次相同按数值升序)
l = SortedList()
# r: 剩余元素的集合
r = SortedList()
# cnt: 记录窗口中每个元素的频次
cnt = Counter()
# s: l 集合中所有元素的 (频次 × 数值) 之和
s = 0
n = len(nums)
ans = [0] * (n - k + 1)
for i, v in enumerate(nums):
# 1. 将当前元素加入窗口
remove(v) # 移除旧状态
cnt[v] += 1 # 频次+1
add(v) # 加入新状态
j = i - k + 1 # 窗口左边界
if j < 0:
continue # 窗口还未形成
# 2. 平衡 l 和 r,确保 l 中恰好有 x 个元素
# 如果 l 中元素不足 x 个,从 r 中补充最大的
while r and len(l) < x:
p = r.pop() # r 中最大的元素
l.add(p)
s += p[0] * p[1] # 加入 l,更新总和
# 如果 l 中元素超过 x 个,将最小的移出到 r
while len(l) > x:
p = l.pop(0) # l 中最小的元素
s -= p[0] * p[1] # 从 l 移除,更新总和
r.add(p)
# 3. 记录当前窗口的 x-sum
ans[j] = s
# 4. 窗口左边界元素移出
remove(nums[j]) # 移除旧状态
cnt[nums[j]] -= 1 # 频次-1
add(nums[j]) # 加入新状态(频次可能为0,add中会处理)
return ans
```
---
复杂度分析
指标 复杂度 说明
时间 `O(n × log k)` 每个元素进出窗口各一次,每次 SortedList 操作 `O(log k)`
空间 `O(k)` Counter 和两个有序集合最多存储窗口中的 `k` 个不同元素
---
关键点说明
1. SortedList 的排序:默认按元组 `(频次, 数值)` 的字典序升序排列。`l[0]` 是 `l` 中最小的(最弱),`r.pop()` 取到 `r` 中最大的(最强)。
2. 先 remove 再 add 的原因:元素频次变化时,它在有序集合中的位置可能改变,必须先移除旧状态 `(旧频次, v)`,再插入新状态 `(新频次, v)`。
3. 边界情况:当窗口中不同元素个数 `< x` 时,`l` 会包含所有不同元素,`r` 为空,此时 x-sum 等于窗口总和,符合题意。
4. 关于 `sortedcontainers`:LeetCode 的 Python3 运行环境默认支持 `sortedcontainers` 库,无需额外安装。本地测试时需要 `pip install sortedcontainers`。
---
验证结果
```
示例 1: nums=[1,1,2,2,3,4,2,3], k=6, x=2
结果: [6, 10, 12] ✓
示例 2: nums=[3,8,7,8,7,5], k=2, x=2
结果: [11, 15, 15, 15, 12] ✓
```
下载完整代码:[leetcode_3321_solution.py](sandbox:///mnt/agents/output/leetcode_3321_solution.py)
更多推荐

所有评论(0)