题目描述
原题链接
元素的 频数 是该元素在一个数组中出现的次数。
给你一个整数数组 nums 和一个整数 k 。在一步操作中,你可以选择 nums 的一个下标,并将该下标对应元素的值增加 1 。
执行最多 k 次操作后,返回数组中最高频元素的 最大可能频数 。
示例
示例 1:
输入:nums = [1,2,4], k = 5
输出:3
解释:对第一个元素执行 3 次递增操作,对第二个元素执 2 次递增操作,此时 nums = [4,4,4] 。
4 是数组中最高频元素,频数是 3 。
示例 2:
输入:nums = [1,4,8,13], k = 5
输出:2
解释:存在多种最优解决方案:
- 对第一个元素执行 3 次递增操作,此时 nums = [4,4,8,13] 。4 是数组中最高频元素,频数是 2 。
- 对第二个元素执行 4 次递增操作,此时 nums = [1,8,8,13] 。8 是数组中最高频元素,频数是 2 。
- 对第三个元素执行 5 次递增操作,此时 nums = [1,4,13,13] 。13 是数组中最高频元素,频数是 2 。
示例 3:
输入:nums = [3,9,6], k = 2
输出:1
题目分析
首先通过分析可以很容易得到最终频数最高的元素一定是数组中原本就有的元素,并且应该优先增加和目标元素最接近的元素(为保证增加的总次数最小)。因此可以用滑动窗口法,具体而言,将数组进行排序,然后遍历每个元素,找到可以在增加次数不超过k的情况下能够改变的窗口的最左的边界。寻找左边界如果采用朴素的线性查找时间复杂度是O(n²),容易超时。因此需要进一步优化。
简单来说,将右边界r右移一位后,左边界l和右边界r代表的窗口对应的总次数肯定会增加,如果增加后的总次数比k大,可以右移左边界来缩小窗口,从而减小改变的总次数,直到总次数小于k。
代码
class Solution {
public:
int maxFrequency(vector<int>& nums, int k) {
sort(nums.begin(), nums.end());
int ans = 1;
long long count= 0;//记录当前窗口对应的元素改变次数
for(int l = 0, r = 1; r < nums.size(); r++){
count += (long long)(r - l) * (nums[r] - nums[r - 1]);//此处用long long类型防止越界
while(count > k){
count -= (nums[r] - nums[l++]);
}
ans = max(ans, r - l + 1);
}
return ans;
}
};