Java实现优先队列:二叉堆详解
作者:mobiledu2502873617 | 来源:互联网 | 2024-11-19 12:52
本文详细介绍了二叉堆的概念及其在Java中的实现方法。二叉堆是一种特殊的完全二叉树,具有堆性质,常用于实现优先队列。
### 一、概念介绍
#### 1. 完全二叉树
完全二叉树是指除了最后一层外,所有其他层都是满的,并且最后一层的节点从左到右依次排列。
#### 2. 堆性质
- **最小堆**:父节点的值小于或等于其子节点的值。
- **最大堆**:父节点的值大于或等于其子节点的值。
![完全二叉树示例](https://img-blog.csdn.net/20170618115514882?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvcGVuZ3FpYW93b2xm/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
由于二叉堆是完全二叉树,因此父节点和子节点的位置存在一定的关系。假设我们将二叉堆的第一个元素放在数组索引为1的位置,则父节点和子节点的位置关系如下:
- 索引为i的左孩子的索引是 (2*i)
- 索引为i的右孩子的索引是 (2*i+1)
- 索引为i的父节点的索引是 (i/2)
因此,我们通常使用数组来实现二叉堆。
### 二、实现思路
#### 1. 插入(上滤)
- 在堆的末尾创建一个空穴。
- 如果新元素可以直接放入该空穴而不违反堆的性质,则插入完成。
- 否则,将空穴的父节点的值移入该空穴,空穴向上移动一步。
- 重复上述过程,直到新元素可以放入空穴中。
![插入示例](https://img-blog.csdn.net/20170618122233991?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvcGVuZ3FpYW93b2xm/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
#### 2. 删除根元素(最小值/最大值)(下滤)
- 将根节点删除,形成一个空穴。
- 将堆的最后一个元素移到空穴中。
- 如果该元素可以直接放入空穴而不违反堆的性质,则删除完成。
- 否则,将空穴的两个子节点中较小的一个移入空穴,空穴向下移动一步。
- 重复上述过程,直到该元素可以放入空穴中。
![删除示例](https://img-blog.csdn.net/20170618122932738?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvcGVuZ3FpYW93b2xm/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
#### 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
推荐阅读
-
本文详细探讨了JVM内存结构的主要组成部分,包括Java虚拟机栈、Java堆、方法区等,并深入分析了HotSpot虚拟机的分代收集策略及其对不同内存区域的管理方式。 ...
[详细]
蜡笔小新 2024-11-19 12:56:18
-
本文详细介绍了Java中常用的字符串截取方法及其应用场景,帮助开发者更好地理解和使用这些方法。 ...
[详细]
蜡笔小新 2024-11-17 18:10:47
-
-
mysql数据库json类型数据,sql server json数据类型 ...
[详细]
蜡笔小新 2024-11-19 11:05:28
-
近期项目需要是实现一个通过筛选选取所需数据刷新表格的功能,因为表格只占页面的一小部分,不希望整个也页面都随之刷新,所以首先想到了使用AJAX来实现。 以下介绍解决方法(请忽视 ...
[详细]
蜡笔小新 2024-11-19 10:11:02
-
目录一、线程名称设置和获取二、线程的sleep()三、线程的interrupt四、join()五、yield()六、wait(),notify(),notifyAll( ...
[详细]
蜡笔小新 2024-11-18 20:33:30
-
2019独角兽企业重金招聘Python工程师标准###1.导入jar包,必须jar包:c3p0、mysql-connector、beans、con ...
[详细]
蜡笔小新 2024-11-18 19:49:32
-
本文详细介绍了HashSet类,它是Set接口的一个实现,底层使用哈希表(实际上是HashMap实例)。HashSet不保证元素的迭代顺序,并且是非线程安全的。 ...
[详细]
蜡笔小新 2024-11-18 16:58:22
-
在Java开发中,保护代码安全是一个重要的课题。由于Java字节码容易被反编译,因此使用代码混淆工具如ProGuard变得尤为重要。本文将详细介绍如何使用ProGuard进行代码混淆,以及其基本原理和常见问题。 ...
[详细]
蜡笔小新 2024-11-18 16:46:17
-
php三角形面积,335宝石大全 ...
[详细]
蜡笔小新 2024-11-18 14:51:57
-
本文介绍如何使用匿名内部类实现工厂模式,通过定义接口和工厂接口来创建不同的服务实现。 ...
[详细]
蜡笔小新 2024-11-18 12:56:50
-
Android异步处理一:使用Thread+Handler实现非UI线程更新UI界面Android异步处理二:使用AsyncTask异步更新UI界面Android异步处理三:Handler+Loope ...
[详细]
蜡笔小新 2024-11-15 19:09:29
-
普通树(每个节点可以有任意数量的子节点)级序遍历 ...
[详细]
蜡笔小新 2024-11-14 18:53:26
-
本文详细介绍了兆芯X86 CPU架构的发展历程,从公司成立背景到关键技术授权,再到具体芯片架构的演进,全面解析了兆芯在国产CPU领域的贡献与挑战。 ...
[详细]
蜡笔小新 2024-11-14 15:04:34
-
Java能否直接通过HTTP将字节流绕过HEAP写入SD卡? ...
[详细]
蜡笔小新 2024-11-08 09:14:47
-
在处理大图片时,PHP 常常会遇到内存溢出的问题。为了避免这种情况,建议避免使用 `setImageBitmap`、`setImageResource` 或 `BitmapFactory.decodeResource` 等方法直接加载大图。这些函数在处理大图片时会消耗大量内存,导致应用崩溃。推荐采用分块处理、图像压缩和缓存机制等策略,以优化内存使用并提高处理效率。此外,可以考虑使用第三方库如 ImageMagick 或 GD 库来处理大图片,这些库提供了更高效的内存管理和图像处理功能。 ...
[详细]
蜡笔小新 2024-11-03 20:31:59
-
mobiledu2502873617
这个家伙很懒,什么也没留下!