当前位置: 代码网 > it编程>编程语言>Java > Java堆与优先级队列示例详解

Java堆与优先级队列示例详解

2026年07月17日 Java 我要评论
前言在 java 开发者的语境里,提起“堆(heap)”,我们第一反应可能是 jvm 内存区域。但那是内存管理概念,我们要聊的是数据结构里的堆——一个为

前言

在 java 开发者的语境里,提起“堆(heap)”,我们第一反应可能是 jvm 内存区域。但那是内存管理概念,我们要聊的是数据结构里的堆——一个为了“极速取最值”而生的天才设计。

一、 为什么需要“堆”?

如果我们要在 100 万个数里实时找到最小的那个,arrays.sort() 慢得像蜗牛,因为它做了大量“维持整体有序”的无用功。

  • 核心痛点:堆不追求全员有序,它只关注最值
  • 定义:一棵完全二叉树(要了解完全二叉树的结构,之所以使用完全二叉树就是因为它的特点,可以根据父节点的下标找到对应左右孩子节点,也可以根据孩子节点找到父节点下标),满足父节点永远优于子节点(大顶堆 ≥\ge,小顶堆 ≤\le)。
  • 真面目:逻辑上是树,物理上是数组。

二、 底层映射:如何在数组里“爬树”?

堆之所以快,是因为它抛弃了复杂的 node 指针,直接利用完全二叉树的连续性,把数据平铺在数组里。

这是手写堆的灵魂公式(假设当前索引为 iii):

  • 找爸爸(parent):parent(i)=(i−1)/2
  • 左儿子(left)left(i)=2i+1
  • 右儿子(right):right(i)=2i+2

这种设计极大地节省了内存,且对 cpu 缓存非常友好。

三、 手写核心算法:堆的“自我修复”

堆的秩序全靠这两个动作维持。在 java 中,建议用迭代(while 循环)代替递归,性能更稳。

1. sift up (向上调整):新人的晋升

新元素插在末尾,不断和父亲比大小,比父亲强就“上位”。

private void siftup(int k) {
    while (k > 0) {
        int parent = (k - 1) / 2;
        if (heap[k] <= heap[parent]) break; // 大顶堆逻辑
        swap(k, parent);
        k = parent;
    }
}

2. sift down (向下调整):老大的退位

弹出堆顶后,把末尾小弟顶上来,然后它要在两个儿子里挑个最强的交换,直到各就各位。

private void siftdown(int k) {
    while (2 * k + 1 < size) {
        int j = 2 * k + 1; 
        if (j + 1 < size && heap[j + 1] > heap[j]) j++; // 挑出更强的儿子
        if (heap[k] >= heap[j]) break;
        swap(k, j);
        k = j;
    }
}

复杂度分析:调整过程只跟树高相关,插入和删除均为 o(logn)。

四、 落地 java:priorityqueue 源码避坑

java 的 priorityqueue 默认是小顶堆。如果你想变大顶堆,得这么写:

priorityqueue<integer> maxheap = new priorityqueue<>(collections.reverseorder());

关键细节:

  • 扩容:小容量时翻倍+2,大容量(>64)时增加 50%。
  • 遍历陷阱:用 for-each 遍历 priorityqueue 结果是无序的(因为它按数组下标打印),想要有序必须不停调用 poll()
  • 线程安全:它是非线程安全的,高并发场景请出门左转找 priorityblockingqueue

五、 实战:用“末位淘汰制”解决 top-k

求前 kkk 个最大的数,反而要用小顶堆

逻辑

  1. 用前 kkk 个数建个小顶堆,堆顶是当前的“守门员”(最小值)。
  2. 后面的数只要比“守门员”大,就踢走堆顶,自己进去调整。
  3. 优势:空间复杂度仅o(k),时间  o(nlog⁡k)。在处理海量日志、实时热搜时,这是唯一可行的方案。

六、 总结

总的来说,堆这种结构在 java 开发中就像是一个“高效的守门员”:它不追求全体有序,只专注于以 o(log⁡n)o(\log n)o(logn) 的极低代价压榨出最值。当你面对海量数据监控、定时任务调度(如 timer 类的底层)或是像 top-k 这种经典的性能瓶颈场景时,priorityqueue 往往是比 arrays.sort() 更聪明的选择。但在使用时也要留个心眼,记住它那“父子有序、兄弟乱序”的脾气,以及非线程安全的局限性。如果需要全序排列,隔壁的 treemap 或许更适合你,但论起处理流式数据的快准狠,堆依然是数据结构里的不二之选。

面试如果被问到“堆”,别只说它是完全二叉树,试着聊聊 siftdown 里的局部交换优化,或者 top-k 里的逆向思维,这可能是拉开差距的地方。

到此这篇关于java堆与优先级队列的文章就介绍到这了,更多相关java堆与优先级队列内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2026  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com