首页 游戏 软件 资讯 排行榜 专题
首页
编程语言
Java数组模拟大顶堆实现与TopK高频元素高效筛选

Java数组模拟大顶堆实现与TopK高频元素高效筛选

热心网友
54
转载
2026-05-07

如何在 Java 中利用数组模拟实现大顶堆(Max-Heap)并完成高效的前 K 个高频元素筛选

如何在 Ja va 中利用数组模拟实现大顶堆(Max-Heap)并完成高效的前 K 个高频元素筛选

免费影视、动漫、音乐、游戏、小说资源长期稳定更新! 👉 点此立即查看 👈

大顶堆(Max-Heap)采用数组实现时,节点 i 的左子节点索引为 2i+1,右子节点为 2i+2,父节点索引为 (i-1)/2,且每个节点的值必须大于或等于其子节点的值。构建堆的核心操作包括 shiftUp(插入后上浮调整)和 shiftDown(堆顶替换后下沉调整)。高效建堆方法是从最后一个非叶子节点开始向前遍历并调用 shiftDown,其时间复杂度为 O(n)。

大顶堆的数组实现原理

大顶堆本质上是一种特殊的完全二叉树结构,采用数组进行存储是实现该数据结构最高效且直观的方式。数组下标与堆节点之间存在固定的映射关系:对于任意下标 i(从 0 开始计数),其左子节点位于 2*i+1,右子节点位于 2*i+2,而父节点可通过 (i-1)/2 进行整数除法计算得出。大顶堆的核心性质是:任意节点的值都大于或等于其左右子节点的值。因此,数组的第一个元素(索引 0)始终代表堆中的最大值,便于快速访问。

手动构建大顶堆的核心操作

将无序数据转化为规范的大顶堆结构,依赖于两个基础且关键的操作步骤:

  • shiftUp(上浮调整):当新元素插入数组末尾时,可能破坏堆的有序性。此时需将其与父节点比较,若值更大则交换位置,并持续向上比较与交换,直至到达根节点或找到合适位置,从而恢复堆性质。
  • shiftDown(下沉调整):当移除堆顶最大值后,通常将数组末尾元素移至堆顶。该元素可能不满足堆条件,需将其与左右子节点中较大的一个进行比较,若小于子节点则交换,并在新的位置上继续向下比较与调整,直至重新满足堆结构要求。

对于直接将无序数组构建为大顶堆,存在一种时间复杂度为 O(n) 的高效算法:从最后一个非叶子节点(索引为 size/2 - 1)开始,向前遍历每个节点,并对每个节点执行一次 shiftDown 操作。该方法比逐个插入元素的 O(n log n) 方法性能更优。

用大顶堆求前 K 个高频元素

“前 K 个高频元素”问题旨在从一组数据中找出出现频率最高的 K 个元素。利用大顶堆解决此问题的步骤清晰高效:

立即学习“Java免费学习笔记(深入)”;

  • 第一步:频率统计。遍历整个数组,使用 HashMap 记录每个元素及其出现的次数,完成频次映射。
  • 第二步:堆构建与操作。将每个“元素-频次”对封装为对象(例如定义 static class Pair { int val; int cnt; }),并以频次 cnt 作为比较依据,将所有 Pair 对象构建成一个大顶堆。
  • 第三步:结果提取。连续执行 K 次 弹出堆顶(poll) 操作——每次取出当前频次最高的元素,随后调整堆结构——所获得的 val 即为所求的前 K 个高频元素。

需要说明的是,若 K 值远小于元素种类数 N,业界更优解是维护一个容量为 K 的小顶堆(Min-Heap)来动态筛选 Top-K。但若题目明确要求使用大顶堆模拟实现,则上述流程完全符合要求,并能清晰展示堆操作的全过程。

关键代码片段示意(不依赖 PriorityQueue)

在不依赖 Java 内置 PriorityQueue 的情况下,我们通过数组手动模拟堆操作,核心是维护堆数组(如 Pair[] heap)和当前堆大小 size。关键方法的代码结构如下:

// 向堆中插入新元素
void add(Pair p) {
heap[size++] = p;
shiftUp(size - 1); // 新元素从末尾开始上浮调整
}

// 取出并移除堆顶最大元素
Pair poll() {
Pair ret = heap[0]; // 保存当前最大值
heap[0] = heap[--size]; // 用末尾元素覆盖堆顶
shiftDown(0); // 新的堆顶元素开始下沉调整
return ret;
}

具体的 shiftUpshiftDown 方法实现,必须严格遵循父子索引关系(2*i+1, 2*i+2, (i-1)/2)及比较逻辑,同时注意边界条件判断(例如子节点索引 child 需小于当前堆大小 size),以防止数组越界异常。

来源:https://www.php.cn/faq/2423341.html
免责声明: 游乐网为非赢利性网站,所展示的游戏/软件/文章内容均来自于互联网或第三方用户上传分享,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系youleyoucom@outlook.com。

相关攻略

Java文件复制教程Filescopy方法实现高效文件与流拷贝
编程语言
Java文件复制教程Filescopy方法实现高效文件与流拷贝

Java的Files copy()方法简洁高效,但使用时需注意细节。默认不覆盖文件,需显式传入REPLACE_EXISTING选项。复制InputStream时,必须用try-with-resources确保流未被提前消费。处理大文件需检查返回值,网络文件系统可能降级缓冲。保留文件属性需指定COPY_ATTRIBUTES,但跨系统或使用流时可能失效。复杂场景

热心网友
05.07
Java文件路径校验指南:如何正确使用NotDirectoryException判断目录
编程语言
Java文件路径校验指南:如何正确使用NotDirectoryException判断目录

在Java中,应主动使用Files isDirectory()等方法预先校验路径是否为有效目录,而非依赖NotDirectoryException进行事后判断。可结合Files exists()和Files isReadable()进行更严谨的检查,以确保后续目录操作顺利进行。避免使用异常处理常规逻辑分支,以提升代码效率和清晰度。

热心网友
05.07
Java浮点数精度判定指南Mathulp方法获取最小精度差详解
编程语言
Java浮点数精度判定指南Mathulp方法获取最小精度差详解

在Java中直接比较浮点数可能导致错误,应使用动态容差。Math ulp(double)方法返回给定数值在浮点表示中相邻值的间距,该值随数值大小变化,为本地化精度单位。通过以较大绝对值为参考计算ulp作为容差,可避免固定epsilon的缺陷,实现更精准的浮点数近似相等判定,尤其适用于科学计算等场景。

热心网友
05.07
Java业务逻辑中利用Math.abs计算数值差绝对值进行阈值判断方法
编程语言
Java业务逻辑中利用Math.abs计算数值差绝对值进行阈值判断方法

在Java业务开发中,使用Math abs(a-b)计算两个数值差的绝对值,是进行阈值判断的简洁高效方法。该方法直接调用标准库,避免了手动比较的冗余和潜在精度问题,适用于温度偏差、时间间隔、库存差异等多种需要容错判断的场景。

热心网友
05.07
Java数组实现多级反馈队列调度算法模拟操作系统任务分配
编程语言
Java数组实现多级反馈队列调度算法模拟操作系统任务分配

使用数组模拟多级反馈队列调度,设置三个优先级队列,高优先级时间片短,新任务由此进入。未完成的任务降级至低优先级队列,同时引入升权机制防止饥饿。通过循环推进CPU时间并按优先级执行任务,记录状态与队列变化,验证了算法对短任务的优待及整体调度行为。

热心网友
05.07

最新APP

宝宝过生日
宝宝过生日
应用辅助 04-07
台球世界
台球世界
体育竞技 04-07
解绳子
解绳子
休闲益智 04-07
骑兵冲突
骑兵冲突
棋牌策略 04-07
三国真龙传
三国真龙传
角色扮演 04-07

热门推荐

Java对象比对防空指针指南Objects.equals方法安全使用详解
编程语言
Java对象比对防空指针指南Objects.equals方法安全使用详解

在Java中直接调用a equals(b)进行对象比较时,若a为null会抛出NullPointerException。使用Objects equals(a,b)方法能自动处理参数为null的情况,其内部通过先检查引用是否为null再调用equals,从而安全地完成比较。该方法适用于实体字段判等等场景,但需注意其将两个null视为相等的设计是否符合具体业务逻

热心网友
05.07
Java子线程崩溃全局捕获与处理指南ThreadsetUncaughtExceptionHandler方法详解
编程语言
Java子线程崩溃全局捕获与处理指南ThreadsetUncaughtExceptionHandler方法详解

全局拦截子线程崩溃需设置默认处理器并结合自定义ThreadFactory为每个新线程注入统一处理器,前者作为兜底方案,但无法覆盖已有专属处理器的线程及Android主线程。Android中还需额外处理主线程及异步框架异常。捕获崩溃后应留存现场、异步上报并防止雪崩。

热心网友
05.07
CMS垃圾收集器详解初始标记并发标记重新标记与并发清除阶段分析
编程语言
CMS垃圾收集器详解初始标记并发标记重新标记与并发清除阶段分析

CMS垃圾收集器以低延迟为目标,其四个阶段中仅初始标记和重新标记需要暂停所有用户线程。初始标记快速标记直接关联对象,重新标记修正并发标记期间变动的引用,两者停顿时间极短。而并发标记和并发清除阶段则与用户线程并行执行,避免了长时间中断。

热心网友
05.07
Java只读缓冲区创建指南ByteBufferasReadOnlyBuffer方法详解与数据保护实践
编程语言
Java只读缓冲区创建指南ByteBufferasReadOnlyBuffer方法详解与数据保护实践

ByteBuffer asReadOnlyBuffer()方法创建原缓冲区的只读视图,共享底层数据且禁止写入,但无法阻止通过其他可写引用修改数据,因此不提供真正的数据隔离。它适用于需只读访问且避免拷贝的场景;若需完全隔离,则应进行深拷贝。

热心网友
05.07
Java单例模式初始化空指针异常ExceptionInInitializerError排查指南
编程语言
Java单例模式初始化空指针异常ExceptionInInitializerError排查指南

ExceptionInInitializerError常包裹单例模式静态初始化时发生的空指针异常。排查需通过getCause()找到根源,通常是静态字段赋值或静态代码块中的空值。应注意静态初始化顺序,避免循环依赖。对于复杂初始化,推荐使用懒汉式并在getInstance()方法内进行异常处理,以便直接定位问题。

热心网友
05.07