描述
给你一个长度为 n 的整数数组 nums ,请你求出每个长度为 k 的子数组的 美丽值 。
一个子数组的 美丽值 定义为:如果子数组中第 x 小整数 是 负数 ,那么美丽值为第 x 小的数,否则美丽值为 0 。
请你返回一个包含 n - k + 1 个整数的数组,依次 表示数组中从第一个下标开始,每个长度为 k 的子数组的 美丽值 。
子数组指的是数组中一段连续 非空 的元素序列。
解题思路
由于值域很小,所以借鉴计数排序,用一个 cnt 数组维护窗口内每个数的出现次数。然后遍历 cnt 去求第 x 小的数。
什么是第 x 小的数?
设它是 num,那么 <num 的数有 <x 个,≤num 的数有 ≥x 个,就说明 num 是第 x 小的数。
比如 [−1,−1,−1] 中,第 1,2,3 小的数都是 −1。
代码实现
public int[] getSubarrayBeauty(int[] nums, int k, int x) {
int n = nums.length;
int[] result = new int[n - k + 1];
// 遍历每个长度为k的子数组
for (int i = 0; i <= n - k; i++) {
// 计数排序数组,假设值域较小,我们可以预设一个合理的范围
int[] count = new int[201]; // 设定值域为[-100, 100] -> 范围大小为201
// 统计当前子数组的每个数
for (int j = i; j < i + k; j++) {
count[nums[j] + 100]++; // 偏移+100,处理负数
}
// 遍历负数,找到第x小的负数
int negativeCount = 0;
int beautyValue = 0;
for (int j = 0; j < 100; j++) { // 负数在[-100, -1]之间
negativeCount += count[j];
if (negativeCount >= x) {
beautyValue = j - 100; // 恢复为原始的负数
break;
}
}
// 如果没有找到第x小的负数,返回0
if (negativeCount < x) {
beautyValue = 0;
}
result[i] = beautyValue;
}
return result;
}
public int[] getSubarrayBeauty2(int[] nums, int k, int x) {
final int BIAS = 50;
int[] cnt = new int[BIAS * 2 + 1];
for (int i = 0; i < k - 1; i++) { // 先往窗口内添加 k-1 个数
cnt[nums[i] + BIAS]++;
}
int n = nums.length;
int[] ans = new int[n - k + 1];
for (int i = k - 1; i < n; i++) {
cnt[nums[i] + BIAS]++; // 进入窗口(保证窗口有恰好 k 个数)
int left = x;
for (int j = 0; j < BIAS; j++) { // 暴力枚举负数范围 [-50,-1]
left -= cnt[j];
if (left <= 0) { // 找到美丽值
ans[i - k + 1] = j - BIAS;
break;
}
}
cnt[nums[i - k + 1] + BIAS]--; // 离开窗口
}
return ans;
}
class Solution:
def getSubarrayBeauty(self, nums: List[int], k: int, x: int) -> List[int]:
cnt = [0] * 101
# 先往窗口添加 k-1 个数
for num in nums[: k - 1]:
cnt[num] += 1
ans = [0] * (len(nums) - k + 1)
for i, (in_, out) in enumerate(zip(nums[k - 1 :], nums)):
# 进入窗口,保证窗口内恰好有 k 个数
cnt[in_] += 1
left = x
for j in range(-50, 0):
# 暴力枚举负数范围
left -= cnt[j]
if left <= 0:
# 找到美丽值
ans[i] = j
break
# 离开窗口
cnt[out] -= 1
return ans
https://leetcode.cn/problems/sliding-subarray-beauty/description/