当前位置: 首页 > news >正文

Kimi LeetCode 3347. 执行操作后元素的最高频率 II C语言实现

LeetCode 3347. 执行操作后元素的最高频率 II — C 语言实现

题目回顾

给定整数数组 `nums` 和两个整数 `k`、`numOperations`。可以执行最多 `numOperations` 次操作,每次选择一个未被选过的下标 `i`,将 `nums[i]` 增加 `[-k, k]` 范围内的任意整数。求操作后数组中最高频率元素的出现次数。

关键约束(II 与 I 的区别):
- `nums[i]` 最大可达 10⁹(I 中是 10⁵)
- `k` 最大可达 10⁹

因此不能用数组遍历值域,需要使用差分数组(离散化)或排序+双指针来优化。

---

核心思路:差分数组(离散化)

每个元素 `x` 可以变成 `[x-k, x+k]` 范围内的任意值。对于某个目标值 `T`:
- 能变成 `T` 的元素个数 = 满足 `x-k ≤ T ≤ x+k` 的元素个数
- 其中原本就等于 `T` 的元素不需要消耗操作次数
- 实际需要操作次数 = `能变成T的元素个数 - 原本等于T的元素个数`
- 最终频率 = `原本等于T的元素个数 + min(实际需要操作次数, numOperations)`

关键洞察:最优的 `T` 一定出现在某个 `nums[i]-k`、`nums[i]` 或 `nums[i]+k` 处。因此可以用差分数组在离散坐标上标记每个元素的覆盖范围,然后通过前缀和统计每个候选点的覆盖数。

---

C 语言代码实现

```c
#include <stdlib.h>

// 用于 qsort 的比较函数
static int cmp_int(const void *a, const void *b) {
int x = *(const int *)a;
int y = *(const int *)b;
if (x < y) return -1;
if (x > y) return 1;
return 0;
}

// 用于 qsort 比较结构体(按坐标排序)
typedef struct {
long long key; // 坐标值(用 long long 防止溢出)
int val; // 差分值
} DiffEntry;

static int cmp_entry(const void *a, const void *b) {
DiffEntry *ea = (DiffEntry *)a;
DiffEntry *eb = (DiffEntry *)b;
if (ea->key < eb->key) return -1;
if (ea->key > eb->key) return 1;
return 0;
}

int maxFrequency(int* nums, int numsSize, int k, int numOperations) {
// 1. 统计每个数字的原始出现频率
// 先对 nums 排序,方便统计频率
int *sorted_nums = (int *)malloc(numsSize * sizeof(int));
for (int i = 0; i < numsSize; i++) {
sorted_nums[i] = nums[i];
}
qsort(sorted_nums, numsSize, sizeof(int), cmp_int);

// 用数组存储 (值, 频率) 对
// 最多 numsSize 个不同的值
long long *freq_keys = (long long *)malloc(numsSize * sizeof(long long));
int *freq_vals = (int *)malloc(numsSize * sizeof(int));
int freq_count = 0;

for (int i = 0; i < numsSize; ) {
int j = i;
while (j < numsSize && sorted_nums[j] == sorted_nums[i]) {
j++;
}
freq_keys[freq_count] = sorted_nums[i];
freq_vals[freq_count] = j - i;
freq_count++;
i = j;
}

// 2. 构建差分数组
// 每个元素产生 3 个关键点:num-k (+1), num (确保存在), num+k+1 (-1)
// 但 num 可能已经存在,所以最多 3*numsSize 个条目
// 实际上 num-k 和 num+k+1 可能重复,我们用数组收集后排序合并
int max_diff_size = numsSize * 3;
DiffEntry *diff = (DiffEntry *)malloc(max_diff_size * sizeof(DiffEntry));
int diff_count = 0;

for (int i = 0; i < numsSize; i++) {
long long num = nums[i];
long long left = num - (long long)k;
long long right = num + (long long)k + 1;

// 标记左端点 +1
diff[diff_count].key = left;
diff[diff_count].val = 1;
diff_count++;

// 标记右端点+1处 -1
diff[diff_count].key = right;
diff[diff_count].val = -1;
diff_count++;
}

// 确保每个原始值在 diff 中存在(值为0)
for (int i = 0; i < freq_count; i++) {
diff[diff_count].key = freq_keys[i];
diff[diff_count].val = 0;
diff_count++;
}

// 3. 按坐标排序差分数组,并合并相同坐标的值
qsort(diff, diff_count, sizeof(DiffEntry), cmp_entry);

// 合并相同 key 的条目
DiffEntry *merged = (DiffEntry *)malloc(diff_count * sizeof(DiffEntry));
int merged_count = 0;

for (int i = 0; i < diff_count; ) {
long long key = diff[i].key;
int sum = 0;
while (i < diff_count && diff[i].key == key) {
sum += diff[i].val;
i++;
}
merged[merged_count].key = key;
merged[merged_count].val = sum;
merged_count++;
}

// 4. 计算前缀和,遍历每个点求最大频率
int ans = 0;
int cover = 0; // 当前前缀和

for (int i = 0; i < merged_count; i++) {
cover += merged[i].val;

long long x = merged[i].key;

// 查找 x 在 freq 中的原始频率
int original = 0;
// 二分查找
int lo = 0, hi = freq_count - 1;
while (lo <= hi) {
int mid = lo + (hi - lo) / 2;
if (freq_keys[mid] == x) {
original = freq_vals[mid];
break;
} else if (freq_keys[mid] < x) {
lo = mid + 1;
} else {
hi = mid - 1;
}
}

// 计算当前坐标能达到的最大频率
int possible = cover;
int limit = original + numOperations;
int current = possible < limit ? possible : limit;

if (current > ans) {
ans = current;
}
}

// 释放内存
free(sorted_nums);
free(freq_keys);
free(freq_vals);
free(diff);
free(merged);

return ans;
}
```

---

代码详解

步骤 说明
统计频率 先对 `nums` 排序,然后遍历统计每个不同值的频率,存储在 `freq_keys`(值)和 `freq_vals`(频率)中
差分标记 对每个 `num`,在 `num-k` 处 `+1`(开始覆盖),在 `num+k+1` 处 `-1`(结束覆盖)。同时确保每个原始值在 diff 中存在
排序合并 将所有差分条目按坐标排序,合并相同坐标的值
前缀和计算 遍历合并后的差分数组,`cover` 表示当前坐标被多少个元素的覆盖范围包含
频率计算 `min(cover, original + numOperations)`:覆盖数受限于"原始个数+可操作次数"
二分查找 用二分查找在 `freq_keys` 中查找当前坐标的原始频率

---

复杂度分析

- 时间复杂度:`O(n log n)`,主要来自排序操作(排序 nums、排序 diff)
- 空间复杂度:`O(n)`,用于存储频率数组和差分数组

---

示例验证

示例 1:`nums = [1,4,5], k = 1, numOperations = 2`

- 元素 1 覆盖 `[0, 2]`,元素 4 覆盖 `[3, 5]`,元素 5 覆盖 `[4, 6]`
- 差分标记:`{0:+1, 3:-1, 3:+1, 6:-1, 4:+1, 7:-1, 1:0, 4:0, 5:0}`
- 排序合并后:`{0:+1, 1:0, 3:0, 4:+1, 5:0, 6:-1, 7:-1}`
- 遍历:
- `x=0`: cover=1, original=0 → `min(1, 0+2)=1`
- `x=1`: cover=1, original=1 → `min(1, 1+2)=1`
- `x=3`: cover=1, original=0 → `min(1, 0+2)=1`
- `x=4`: cover=2, original=1 → `min(2, 1+2)=2` ✓
- `x=5`: cover=2, original=1 → `min(2, 1+2)=2`
- `x=6`: cover=1, original=0 → `min(1, 0+2)=1`
- 答案 = 2

示例 2:`nums = [5,11,20,20], k = 5, numOperations = 1`

- 元素 5 覆盖 `[0,10]`,11 覆盖 `[6,16]`,20 覆盖 `[15,25]`(两个20)
- 在 `x=20` 处:cover=2(两个20都能覆盖20),original=2 → `min(2, 2+1)=2` ✓
- 答案 = 2

http://www.gsyq.cn/news/1584695.html

相关文章:

  • 【第十期】高级进阶篇:自动化与智能化 —— 如何用 Python 和 AI 辅助挖掘漏洞?
  • 2026-06-23:合并靠近字符。用go语言,现有仅含小写字母的字符串s与整数k,规则说明如下: 1. 判定标准:同一字符串里,若两个相同字母的位置索引差值不超过k,这两个字符视作相邻靠近字符。 2
  • HarmonyOS 6商城开发学习:平板竖屏下的底部“飞件“事故——用 layoutWeight 替掉 position 与 Stack 的响应式救火
  • 项目实训(十一)| 学习路线模块:个性化学习路线生成
  • 【Linux基础】Linux 必学基础指令:echo/cat/ 重定向 / 查找命令全解析
  • 阿里通义千问,8元叠加券,真的可以领到,真没有套路,真不用拉人头,实打实的,就是这么简单!
  • 信创业务技术全景解析:从项目实施到国密安全,一文读懂信创落地核心技术体系(PPT)
  • 《个人头像上传》二、Preferences用户首选项使用指南
  • TVA在机电产品视觉检测的创新应用(11)
  • 华为OD机试真题-预测新能源发电量(C/C++/Py/Java/Js/Go)
  • MacBook的实用小技巧
  • 高股息投资笔记-股票的人性2
  • 2 建立连接
  • LIVE项目解析:基于图像先验与时间一致性的AI视频编辑技术
  • 研发与业务协同工具怎么选?2026 主流团队云存储架构深度横评与避坑指南
  • [崛起]大国纪录片系列合集
  • 极小超曲面与Yau猜想:对称流形中的无限存在性定理
  • 2026新能源下乡155款车型全拆解:从625亿国补到铁锂涨价,全产业链机会地图
  • 百考通AI,论文降重与去AI痕迹,更安心,让数据为你说话
  • 东南亚多人手游区域 CDN 调优实战:新加坡、曼谷本地边缘节点降低联机延迟、过滤 UDP 异常流量
  • 视觉语言模型中的熵梯度证据定位技术解析
  • 基于通路交互图与GNN的多组学癌症转移预测模型构建指南
  • LLM提示词工程2.0:从Prompt到Prompt DSL的范式演进2026
  • RAP 里的 managed 与 unmanaged,别把它们理解成自动档和手动档那么简单
  • Linux环境下部署Zookeeper3.9.5(最新版)集群部署
  • 基于MobileNetV3的轻量化人脸年龄估计模型构建与移动端部署实战
  • 【学习心得 ● 运维】nginx 常用命令(烦人的Nginx)
  • DOSE:基于现成模型的多模态LLM训练数据筛选实战指南
  • DNA动力学可视化:深度学习与生物物理信息融合的ViDa框架解析
  • 大语言模型参数恢复的数学框架与实现