Java 中的堆实现

2025年8月4日 | 阅读 16 分钟

在 Java 中,堆是一种特殊的数据结构,其中根节点或父节点与左右子节点进行比较,并根据顺序排列。假设 x 是一个根节点,y 是一个子节点,属性key(x) <= key(y) 会生成最小堆,并且这种关系被称为“堆属性”

根据父节点和子节点的顺序,堆可以分为两种形式,即最小堆和最大堆。让我们一个一个地理解它们,并在 Java 中实现代码。

堆的操作

插入:将新元素添加到堆(数组)的末尾,然后“向上堆化”,通过将添加的元素与其父节点进行比较并根据需要进行交换,直到找到正确的位置,以维护堆属性。

删除:删除根元素(最小堆的最小值,最大堆的最大值),用堆中的最后一个元素替换它,然后“向下堆化”,通过将新的根与其子节点进行比较并根据需要进行交换,直到找到正确的位置。

堆化:调整堆以维护堆属性。在向上堆化中,将新添加的元素与其父节点进行比较,并在需要时进行交换。在向下堆化中,将根元素与其子节点进行比较,并在需要时进行交换。此过程确保堆仍然是有效的最小堆或最大堆。

最小堆

最小堆是一种特殊的堆数据结构,它本身是一个完全二叉树。最小堆具有以下属性

  1. 根节点的值总是小于堆中的其他节点。
  2. 每个内部节点的值总是小于或等于其子节点。

我们可以在最小堆中执行以下三个操作

insertNode()

我们可以通过将新键添加到树的末尾来在最小堆中执行插入。如果插入键的值小于其父节点,我们必须向上遍历键以满足堆属性。插入过程需要 O(log n) 时间。

extractMin()

这是最重要的操作之一,我们通过它来删除最小值节点,即堆的根节点。删除根节点后,我们必须确保堆属性得到维护。extractMin() 操作需要 O(Logn) 时间来从堆中删除最小值。

getMin()

getMin() 操作用于以 O(1) 时间获取堆的根节点,即最小值。

示例

Heap implementation in Java

最小堆算法

MinHeapJavaImplementation.java

输出

Heap implementation in Java

使用数组

最小堆是一种二叉树,其中父节点的值总是小于其子节点的值,从而确保最小值位于根节点。使用数组在 Java 中实现最小堆可以通过简单的索引算术来实现高效的插入、删除和堆化。这种结构非常适合需要快速访问最小元素的应用程序,例如优先队列。

文件名:RefactoredMinHeap.java

输出

The Min Heap is: [3, 13, 7, 16, 21, 12, 9]
Parent : 3 Left : 13 Right : 7
Parent : 13 Left : 16 Right : 21
Parent : 7 Left : 12 Right : 9

The Min Value is: 3

The Min Heap is: [7, 13, 9, 16, 21, 12, 9]
Parent : 7 Left : 13 Right : 9
Parent : 13 Left : 16 Right : 21
Parent : 9 Left : 12

最大堆

最大堆是另一种特殊的堆数据结构,它在 Java 中也是一个完全二叉树。最大堆具有以下属性

  1. 根节点的值总是大于堆中的其他节点。
  2. 每个内部节点的值总是大于或等于其子节点。

我们可以在最大堆中执行以下三个操作

insertNode()

我们可以通过将新键添加到树的末尾来在最大堆中执行插入。如果插入键的值大于其父节点,我们必须向上遍历键以满足堆属性。插入过程需要 O(log n) 时间。

extractMax()

这是最重要的操作之一,我们通过它来删除最大值节点,即堆的根节点。删除根节点后,我们必须确保堆属性得到维护。extractMax() 操作需要 O(Log n) 时间来从堆中删除最大值。

getMax()

getMax() 操作用于以 O(1) 时间获取堆的根节点,即最大值。

示例

Heap implementation in Java

最小堆算法

MaxHeapJavaImplementation.java

输出

Heap implementation in Java

使用 Collections.reverseOrder() 方法通过库函数

Java 中的 Collections.reverseOrder() 方法是一个强大的实用程序,用于反转集合中元素的自然顺序。此方法返回一个比较器,该比较器对实现 Comparable 接口的对象集合施加与自然顺序相反的顺序。通过利用此方法,开发人员可以轻松地将最小堆转换为最大堆,反转已排序列表的顺序,或调整优先队列中元素的优先级。

文件名:MaxHeapDemo.java

输出

Head value using peek function:400
The queue elements:
400
30
20
10
After removing an element with poll function:
30
10
20
After removing 30 with remove function:
20
10
Priority queue contains 20 or not?: true
Values in array: 
Value: 20
Value: 10

堆的应用

优先队列:堆通常用于实现优先队列,其中优先级最高的元素首先被提取。这在任务调度、中断处理和事件处理等应用程序中至关重要。

排序算法:堆排序是一种基于比较的排序算法,它依赖于堆数据结构。它的时间复杂度为 O(n log n),因此对于对大型数据集进行排序非常有效。

图算法:堆在 Dijkstra 的最短路径算法、Prim 的最小生成树算法和 A* 搜索算法等图算法中是不可或缺的。

文件压缩:堆在霍夫曼编码等数据压缩技术中起着至关重要的作用。在这里,一个优先队列(通常实现为最小堆)用于构建霍夫曼树。

动态规划:堆在动态规划算法中使用,特别是在贪心算法中,其中元素根据其优先级进行处理。

医疗应用:在医疗环境中,堆用于管理患者信息,包括生命体征、治疗和检查结果,这些信息根据其危急程度或紧急程度进行管理。

外部排序:堆用于外部排序算法,以有效地对超出内存容量的大型数据集进行排序。数据块使用优先队列进行处理。

负载均衡:堆应用于负载均衡算法,以在服务器之间分配任务或请求。任务通常根据其优先级或负载进行处理。

在线算法:堆在处理实时到达数据的在线算法中使用,例如在推荐系统、事件处理和流数据管理中。

金融应用:堆用于股票市场分析和算法交易等金融应用程序,它们根据优先级管理和处理股票数据。

堆的优点

高效的插入和删除:堆结构支持高效的元素插入和删除。当插入新元素时,它最初放在堆的末尾,然后使用堆化操作移动到其正确的位置。同样,当删除元素时,它被堆中的最后一个元素替换,并使用堆化操作重新组织堆。

高效的优先队列实现:堆通常用于实现优先队列,其中最高优先级的元素始终位于堆的顶部。这允许对最高优先级元素进行常数时间访问,从而使堆成为实现优先队列的高效数据结构。

保证访问极端值:在最大堆中,最大元素始终位于根部,而在最小堆中,最小元素始终位于根部。此属性确保堆提供对最大或最小元素的保证访问,这在需要快速访问极端值的算法中很有用。

空间效率:堆结构是空间高效的,因为它将元素组织在一个完全二叉树中,与链表或动态数组等其他数据结构相比,它通常占用更少的内存。

堆排序算法:堆数据结构是堆排序算法的基础,堆排序是一种有效的方法,其最坏情况时间复杂度为 O(n log n)。这使得堆排序成为对大型数据集进行排序的稳健选择。

堆的缺点

灵活性差:堆结构旨在维护元素的特定顺序,这限制了其灵活性。这使其不太适合需要更灵活数据结构的应用程序。

搜索效率低下:虽然堆提供了对顶部元素的有效访问,但它们并不适合搜索特定元素。在堆中查找元素需要遍历整个树,导致时间复杂度为 O(n)。

不稳定性:堆不是稳定的数据结构,这意味着具有相等值的元素的相对顺序在插入或修改过程中可能不会保留。

内存管理挑战:堆需要动态内存分配,这在内存资源有限的系统中可能会出现问题。此外,管理堆的分配内存可能很复杂,并且容易出错。

复杂性考虑:虽然堆允许高效的插入、删除和优先队列操作,但最坏情况时间复杂度为 O(n log n)。对于需要更快的算法的应用程序来说,这可能不是最佳选择。


下一个主题Java-arraylist-length