题目描述
给你一个下标从 0 开始的整数数组 nums
和一个整数 k
。
你可以对数组执行 至多 k
次操作:
- 从数组中选择一个下标
i
,将 nums[i]
增加 或者 减少 1
。
最终数组的频率分数定义为数组中众数的 频率 。
请你返回你可以得到的 最大 频率分数。
众数指的是数组中出现次数最多的数。一个元素的频率指的是数组中这个元素的出现次数。
示例 1:
输入:nums = [1,2,6,4], k = 3
输出:3
解释:我们可以对数组执行以下操作:
- 选择 i = 0 ,将 nums[0] 增加 1 。得到数组 [2,2,6,4] 。
- 选择 i = 3 ,将 nums[3] 减少 1 ,得到数组 [2,2,6,3] 。
- 选择 i = 3 ,将 nums[3] 减少 1 ,得到数组 [2,2,6,2] 。
元素 2 是最终数组中的众数,出现了 3 次,所以频率分数为 3 。
3 是所有可行方案里的最大频率分数。
示例 2:
输入:nums = [1,4,4,2,4], k = 0
输出:3
解释:我们无法执行任何操作,所以得到的频率分数是原数组中众数的频率 3 。
提示:
1 <= nums.length <= 105
1 <= nums[i] <= 109
0 <= k <= 1014
解法
方法一:排序 + 前缀和 + 二分查找
题目求的是在最多进行 $k$ 次操作的情况下,我们能得到的众数的最大频率。如果我们将数组 $nums$ 按照从小到大的顺序排列,那么最好就是将一段连续的数字都变成同一个数,这样可以使得操作次数较少,并且众数的频率较高。
因此,我们不妨先对数组 $nums$ 进行排序。
接下来,我们再分析,如果一个频率 $x$ 是可行的,那么对于任意 $y \le x$,频率 $y$ 也是可行的,这存在着单调性。因此,我们可以通过二分查找,找到最大的满足条件的频率。
我们二分枚举频率,定义二分查找的左边界 $l = 0$,右边界 $r = n$,其中 $n$ 是数组的长度。每次二分查找的过程中,我们取中间值 $mid = \lfloor \frac{l + r + 1}{2} \rfloor$,然后判断 $nums$ 中是否存在一个长度为 $mid$ 的连续子数组,使得这个子数组中的所有元素都变成这个子数组的中位数,且操作次数不超过 $k$。如果存在,那么我们就将左边界 $l$ 更新为 $mid$,否则我们就将右边界 $r$ 更新为 $mid - 1$。
为了判断是否存在这样的子数组,我们可以使用前缀和。我们首先定义两个指针 $i$ 和 $j$,初始时 $i = 0$, $j = i + mid$。那么 $nums[i]$ 到 $nums[j - 1]$ 这一段的元素都变成 $nums[(i + j) / 2]$,所需要的操作次数为 $left + right$,其中:
$$
\begin{aligned}
\textit{left} &= \sum_{k = i}^{(i + j) / 2 - 1} (nums[(i + j) / 2] - nums[k]) \
&= ((i + j) / 2 - i) \times nums[(i + j) / 2] - \sum_{k = i}^{(i + j) / 2 - 1} nums[k]
\end{aligned}
$$
$$
\begin{aligned}
\textit{right} &= \sum_{k = (i + j) / 2 + 1}^{j} (nums[k] - nums[(i + j) / 2]) \
&= \sum_{k = (i + j) / 2 + 1}^{j} nums[k] - (j - (i + j) / 2) \times nums[(i + j) / 2]
\end{aligned}
$$
我们可以通过前缀和数组 $s$ 来计算 $\sum_{k = i}^{j} nums[k]$,从而在 $O(1)$ 的时间内计算出 $left$ 和 $right$。
时间复杂度 $O(n \times \log n)$,空间复杂度 $O(n)$。其中 $n$ 是数组的长度。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 | class Solution:
def maxFrequencyScore(self, nums: List[int], k: int) -> int:
nums.sort()
s = list(accumulate(nums, initial=0))
n = len(nums)
l, r = 0, n
while l < r:
mid = (l + r + 1) >> 1
ok = False
for i in range(n - mid + 1):
j = i + mid
x = nums[(i + j) // 2]
left = ((i + j) // 2 - i) * x - (s[(i + j) // 2] - s[i])
right = (s[j] - s[(i + j) // 2]) - ((j - (i + j) // 2) * x)
if left + right <= k:
ok = True
break
if ok:
l = mid
else:
r = mid - 1
return l
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34 | class Solution {
public int maxFrequencyScore(int[] nums, long k) {
Arrays.sort(nums);
int n = nums.length;
long[] s = new long[n + 1];
for (int i = 1; i <= n; i++) {
s[i] = s[i - 1] + nums[i - 1];
}
int l = 0, r = n;
while (l < r) {
int mid = (l + r + 1) >> 1;
boolean ok = false;
for (int i = 0; i <= n - mid; i++) {
int j = i + mid;
int x = nums[(i + j) / 2];
long left = ((i + j) / 2 - i) * (long) x - (s[(i + j) / 2] - s[i]);
long right = (s[j] - s[(i + j) / 2]) - ((j - (i + j) / 2) * (long) x);
if (left + right <= k) {
ok = true;
break;
}
}
if (ok) {
l = mid;
} else {
r = mid - 1;
}
}
return l;
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37 | class Solution {
public:
int maxFrequencyScore(vector<int>& nums, long long k) {
sort(nums.begin(), nums.end());
int n = nums.size();
vector<long long> s(n + 1, 0);
for (int i = 1; i <= n; i++) {
s[i] = s[i - 1] + nums[i - 1];
}
int l = 0, r = n;
while (l < r) {
int mid = (l + r + 1) >> 1;
bool ok = false;
for (int i = 0; i <= n - mid; i++) {
int j = i + mid;
int x = nums[(i + j) / 2];
long long left = ((i + j) / 2 - i) * (long long) x - (s[(i + j) / 2] - s[i]);
long long right = (s[j] - s[(i + j) / 2]) - ((j - (i + j) / 2) * (long long) x);
if (left + right <= k) {
ok = true;
break;
}
}
if (ok) {
l = mid;
} else {
r = mid - 1;
}
}
return l;
}
};
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34 | func maxFrequencyScore(nums []int, k int64) int {
sort.Ints(nums)
n := len(nums)
s := make([]int64, n+1)
for i := 1; i <= n; i++ {
s[i] = s[i-1] + int64(nums[i-1])
}
l, r := 0, n
for l < r {
mid := (l + r + 1) >> 1
ok := false
for i := 0; i <= n-mid; i++ {
j := i + mid
x := int64(nums[(i+j)/2])
left := (int64((i+j)/2-i) * x) - (s[(i+j)/2] - s[i])
right := (s[j] - s[(i+j)/2]) - (int64(j-(i+j)/2) * x)
if left+right <= k {
ok = true
break
}
}
if ok {
l = mid
} else {
r = mid - 1
}
}
return l
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32 | function maxFrequencyScore(nums: number[], k: number): number {
nums.sort((a, b) => a - b);
const n = nums.length;
const s: number[] = Array(n + 1).fill(0);
for (let i = 1; i <= n; i++) {
s[i] = s[i - 1] + nums[i - 1];
}
let l: number = 0;
let r: number = n;
while (l < r) {
const mid: number = (l + r + 1) >> 1;
let ok: boolean = false;
for (let i = 0; i <= n - mid; i++) {
const j = i + mid;
const x = nums[Math.floor((i + j) / 2)];
const left = (Math.floor((i + j) / 2) - i) * x - (s[Math.floor((i + j) / 2)] - s[i]);
const right = s[j] - s[Math.floor((i + j) / 2)] - (j - Math.floor((i + j) / 2)) * x;
if (left + right <= k) {
ok = true;
break;
}
}
if (ok) {
l = mid;
} else {
r = mid - 1;
}
}
return l;
}
|