leetcode-1838-最高频元素的频数
今天搬家来晚了些,早就已经写完了,一直在跑二分的数据集+搬家,本题的数据感觉真的不适合二分
[博客链接]
[题目描述]
元素的 频数 是该元素在一个数组中出现的次数。
给你一个整数数组 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
提示:
1 <= nums.length <= 105
1 <= nums[i] <= 105
1 <= k <= 105
Related Topics 数组 二分查找 前缀和 滑动窗口
👍 64 👎 0
[题目链接]
[github地址]
[思路介绍]
思路一:前缀和+暴力扫描+排序
- 可以思考一个问题就是我们操作变化的元素会不会超过数组的最大元素
- 如果我们变大了最大元素,且最大元素是最高频次元素,那其余操作都要因此增加
- 因此我们可以假定我们不需要变大最大元素* 这样就确立了一个二分范围
- 第一个元素的元素和 和所有元素的元素和
- 先用暴力法看一下,不走二分,直接全部遍历一次
- 通过前缀和+k与元素值* 元素数量做比较,
- res表示当前元素sum[i]+k >=sum[i-res-1] + (res+1) * nums[i-1]
- 满足则取元素数量* 然后递增res知道res=i即可 最后返回res
public int maxFrequency(int[] nums, int k) {
Arrays.sort(nums);
int n = nums.length;
int res = 1;
int[] sums = new int[n + 1];
sums[1] = nums[0];
for (int i = 2; i <= n; i++) {
sums[i] = sums[i - 1] + nums[i - 1];
}
//i表示i个元素
for (int i = 1; i <= n; i++) {
//至少能保证res+1个元素满足才更新
while (res < i && sums[i] + k >= sums[i - res - 1] + (res + 1) * nums[i - 1]) {
res++;
}
}
return res;
}
时间复杂度
**n表示数组长度
思路二:思路一+二分
- 其实思路一已经提到了二分只是没有实现
class Solution {
int[] nums, sum;
int n, k;
public int maxFrequency(int[] _nums, int _k) {
nums = _nums;
k = _k;
n = nums.length;
Arrays.sort(nums);
sum = new int[n + 1];
for (int i = 1; i <= n; i++) sum[i] = sum[i - 1] + nums[i - 1];
int l = 0, r = n;
while (l < r) {
int mid = l + r + 1 >> 1;
if (check(mid)) l = mid;
else r = mid - 1;
}
return r;
}
boolean check(int len) {
for (int l = 0; l + len - 1 < n; l++) {
int r = l + len - 1;
int cur = sum[r + 1] - sum[l];
int t = nums[r] * len;
if (t - cur <= k) return true;
}
return false;
}
}
时间复杂度
不过我发现,二分反倒容易TLE,感觉应该是中间值的计算在这个数据集里更多