Java实现优先队列:二叉堆详解
作者:mobiledu2502873617 | 来源:互联网 | 2024-11-19 12:52
本文详细介绍了二叉堆的概念及其在Java中的实现方法。二叉堆是一种特殊的完全二叉树,具有堆性质,常用于实现优先队列。
### 一、概念介绍
#### 1. 完全二叉树
完全二叉树是指除了最后一层外,所有其他层都是满的,并且最后一层的节点从左到右依次排列。
#### 2. 堆性质
- **最小堆**:父节点的值小于或等于其子节点的值。
- **最大堆**:父节点的值大于或等于其子节点的值。

由于二叉堆是完全二叉树,因此父节点和子节点的位置存在一定的关系。假设我们将二叉堆的第一个元素放在数组索引为1的位置,则父节点和子节点的位置关系如下:
- 索引为i的左孩子的索引是 (2*i)
- 索引为i的右孩子的索引是 (2*i+1)
- 索引为i的父节点的索引是 (i/2)
因此,我们通常使用数组来实现二叉堆。
### 二、实现思路
#### 1. 插入(上滤)
- 在堆的末尾创建一个空穴。
- 如果新元素可以直接放入该空穴而不违反堆的性质,则插入完成。
- 否则,将空穴的父节点的值移入该空穴,空穴向上移动一步。
- 重复上述过程,直到新元素可以放入空穴中。

#### 2. 删除根元素(最小值/最大值)(下滤)
- 将根节点删除,形成一个空穴。
- 将堆的最后一个元素移到空穴中。
- 如果该元素可以直接放入空穴而不违反堆的性质,则删除完成。
- 否则,将空穴的两个子节点中较小的一个移入空穴,空穴向下移动一步。
- 重复上述过程,直到该元素可以放入空穴中。

#### 3. 创建堆
- **简单方法**:使用N个相继的插入操作来构建堆,每个插入操作的时间复杂度为O(logN),总时间复杂度为O(N logN)。
- **高效方法**:先保持堆的结构性,然后通过下滤操作确保堆的性质。这种方法的时间复杂度为O(N)。
### 三、代码实现
```java
import java.util.ArrayList;
/**
* 头元素存储于1位置
* i节点的左孩子位置2*i,右孩子位置2*i+1,父亲位置i/2
*/
public class BinaryHeap> {
private ArrayList array; // 存储信息
private int currentSize; // 当前大小
public BinaryHeap() {
array = new ArrayList<>();
array.add(null);
}
/**
* 将数组转化为二叉堆
* @param array
*/
public BinaryHeap(ArrayList array) {
currentSize = array.size();
for (int i = 1; i <= currentSize; i++) {
this.array.set(i, array.get(i - 1)); // 保证结构性
}
for (int i = currentSize / 2; i > 0; i--) {
percolateDown(i); // 保证堆性
}
}
/**
* 插入
* @param x
*/
public void insert(T x) {
int hole = currentSize + 1; // 空穴的初始位置
array.add(x);
for (; hole > 1 && x.compareTo(array.get(hole / 2)) <0; hole /= 2) {
array.set(hole, array.get(hole / 2)); // 上滤
}
array.set(hole, x); // 找到合适的位置
currentSize++;
}
/**
* 查找最小元素
* @return
*/
public T findMin() {
return array.get(1);
}
/**
* 删除最小元素
* @return
*/
public T deleteMin() {
if (currentSize <1) {
System.out.println("BinaryHeap is Empty");
}
T minElement = array.get(1); // 获取最小元素
array.set(1, array.get(currentSize)); // 将末尾元素存入
array.remove(currentSize--); // 移除最后元素,并使大小-1
if (currentSize > 0) {
percolateDown(1); // 下滤
}
return minElement;
}
/**
* 删除任一元素
* @return 元素不存在,返回-1;删除成功,返回该元素的下标
*/
public int delete(T x) throws Exception {
if (currentSize <1) {
throw new Exception("BinaryHeap is Empty");
}
int index = array.indexOf(x); // 获取x的索引
if (index == -1) {
return -1;
}
array.set(index, array.get(currentSize));
array.remove(currentSize--);
percolateDown(index); // 下滤
return index;
}
/**
* 下滤
*/
public void percolateDown(int hole) {
int child;
T temp = array.get(hole); // 需要下滤的元素,临时存储
for (; hole * 2 <= currentSize; hole = child) {
child = hole * 2;
if (child 0) {
child++; // child不为最后一个元素,且右元素小于左元素时:child为右元素,否则child为左元素
}
if (temp.compareTo(array.get(child)) > 0) {
array.set(hole, array.get(child)); // temp大于较小的元素,将空穴下滤一层
} else {
break; // 找到合适的位置,跳出循环替换
}
}
array.set(hole, temp);
}
public static void main(String[] args) {
int numItems = 1000;
BinaryHeap h = new BinaryHeap<>();
int i = 37;
for (i = 37; i != 0; i = (i + 37) % numItems) {
h.insert(i);
}
for (i = 1; i
推荐阅读
-
本文探讨了Java中类成员的初始化顺序、静态引入、可变参数以及finalize方法的应用。通过具体的代码示例,详细解释了这些概念及其在实际编程中的使用。 ...
[详细]
蜡笔小新 2024-12-27 19:39:42
-
本文介绍了Java并发库中的阻塞队列(BlockingQueue)及其典型应用场景。通过具体实例,展示了如何利用LinkedBlockingQueue实现线程间高效、安全的数据传递,并结合线程池和原子类优化性能。 ...
[详细]
蜡笔小新 2024-12-27 18:51:49
-
-
本文探讨了 Objective-C 中的一些重要语法特性,包括 goto 语句、块(block)的使用、访问修饰符以及属性管理等。通过实例代码和详细解释,帮助开发者更好地理解和应用这些特性。 ...
[详细]
蜡笔小新 2024-12-26 19:42:38
-
本文详细介绍了 Java 中的 IO 流,包括字节流和字符流的基本概念及其操作方式。探讨了如何处理不同类型的文件数据,并结合编码机制确保字符数据的正确读写。同时,文中还涵盖了装饰设计模式的应用,以及多种常见的 IO 操作实例。 ...
[详细]
蜡笔小新 2024-12-26 17:37:25
-
本文介绍了如何通过 Maven 依赖引入 SQLiteJDBC 和 HikariCP 包,从而在 Java 应用中高效地连接和操作 SQLite 数据库。文章提供了详细的代码示例,并解释了每个步骤的实现细节。 ...
[详细]
蜡笔小新 2024-12-26 17:34:42
-
本文详细介绍如何使用Python进行配置文件的读写操作,涵盖常见的配置文件格式(如INI、JSON、TOML和YAML),并提供具体的代码示例。 ...
[详细]
蜡笔小新 2024-12-28 08:39:55
-
主要用了2个类来实现的,话不多说,直接看运行结果,然后在奉上源代码1.Index.javaimportjava.awt.Color;im ...
[详细]
蜡笔小新 2024-12-27 18:18:10
-
本文详细介绍了Java编程语言中的核心概念和常见面试问题,包括集合类、数据结构、线程处理、Java虚拟机(JVM)、HTTP协议以及Git操作等方面的内容。通过深入分析每个主题,帮助读者更好地理解Java的关键特性和最佳实践。 ...
[详细]
蜡笔小新 2024-12-27 13:55:14
-
本文介绍了如何在C#中启动一个应用程序,并通过枚举窗口来获取其主窗口句柄。当使用Process类启动程序时,我们通常只能获得进程的句柄,而主窗口句柄可能为0。因此,我们需要使用API函数和回调机制来准确获取主窗口句柄。 ...
[详细]
蜡笔小新 2024-12-27 03:39:09
-
本文详细解析了Python中的os和sys模块,介绍了它们的功能、常用方法及其在实际编程中的应用。 ...
[详细]
蜡笔小新 2024-12-26 22:04:19
-
本文探讨了如何在给定整数N的情况下,找到两个不同的整数a和b,使得它们的和最大,并且满足特定的数学条件。 ...
[详细]
蜡笔小新 2024-12-26 19:26:18
-
本文详细介绍了Java中的访问器(getter)和修改器(setter),探讨了它们在保护数据完整性、增强代码可维护性方面的重要作用。通过具体示例,展示了如何正确使用这些方法来控制类属性的访问和更新。 ...
[详细]
蜡笔小新 2024-12-26 17:25:24
-
题目Link题目学习link1题目学习link2题目学习link3%%%受益匪浅!----- ...
[详细]
蜡笔小新 2024-12-26 15:55:56
-
本文详细探讨了VxWorks操作系统中双向链表和环形缓冲区的实现原理及使用方法,通过具体示例代码加深理解。 ...
[详细]
蜡笔小新 2024-12-26 13:26:16
-
本文详细介绍了Java泛型的概念及其在JDK 5中的应用,通过具体代码示例解释了泛型的引入、作用和优势。同时,探讨了泛型类、泛型方法和泛型接口的实现,并深入讲解了通配符的使用。 ...
[详细]
蜡笔小新 2024-12-26 11:15:56
-
mobiledu2502873617
这个家伙很懒,什么也没留下!