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
推荐阅读
-
java文本编辑器,java文本编辑器设计思路 ...
[详细]
蜡笔小新 2024-12-19 21:02:48
-
本题要求在一组数中反复取出两个数相加,并将结果放回数组中,最终求出最小的总加法代价。这是一个经典的哈夫曼编码问题,利用贪心算法可以有效地解决。 ...
[详细]
蜡笔小新 2024-12-20 23:20:38
-
-
本文详细介绍了 Android 中基于消息传递的进程间通信(IPC)机制——Messenger。通过实例和源码分析,帮助开发者更好地理解和使用这一高效的通信工具。 ...
[详细]
蜡笔小新 2024-12-21 11:11:40
-
本文探讨了Java中的多线程并发处理机制,从基本概念到实际应用,帮助读者全面理解并掌握多线程编程技巧。通过实例解析和理论阐述,确保初学者也能轻松入门。 ...
[详细]
蜡笔小新 2024-12-20 19:28:45
-
本报告详细记录了2017-2018学年《网络编程与安全》课程第五次实验的具体内容、实验过程、遇到的问题及解决方案。 ...
[详细]
蜡笔小新 2024-12-20 08:38:38
-
本文探讨了如何通过一系列技术手段提升Spring Boot项目的并发处理能力,解决生产环境中因慢请求导致的系统性能下降问题。 ...
[详细]
蜡笔小新 2024-12-19 21:07:12
-
本文探讨了在 SQL Server 中使用 JDBC 插入数据时遇到的问题。通过详细分析代码和数据库配置,提供了解决方案并解释了潜在的原因。 ...
[详细]
蜡笔小新 2024-12-21 09:52:27
-
本文详细探讨了Java多线程与并发库的高级应用,结合空中网在挑选实习生时的面试题目,深入分析了相关技术要点和实现细节。文章通过具体的代码示例展示了如何使用Semaphore和SynchronousQueue来管理线程同步和任务调度。 ...
[详细]
蜡笔小新 2024-12-21 09:43:07
-
蜡笔小新 2024-12-20 20:43:56
-
本文深入探讨了MySQL中常见的面试问题,包括事务隔离级别、存储引擎选择、索引结构及优化等关键知识点。通过详细解析,帮助读者在面对BAT等大厂面试时更加从容。 ...
[详细]
蜡笔小新 2024-12-20 18:58:01
-
先上效果:主要实现了Listview的绑定和点击事件。项目资源结构如下:先创建一个动物类,用来装载数据:Animal类如下:packagecom.example.simplelis ...
[详细]
蜡笔小新 2024-12-20 18:17:25
-
要做一个简单的保存网页界面,首先用jsp写出保存界面,本次界面比较简单,首先是三个提示语,后面是三个输入框,然 ...
[详细]
蜡笔小新 2024-12-20 15:00:51
-
本文详细探讨了Java中的ClassLoader类加载器的工作原理,包括其如何将class文件加载至JVM中,以及JVM启动时的动态加载策略。文章还介绍了JVM内置的三种类加载器及其工作方式,并解释了类加载器的继承关系和双亲委托机制。 ...
[详细]
蜡笔小新 2024-12-20 12:58:21
-
本文详细解析了Java中throw和throws的关键区别,同时涵盖了JDK的定义、Java虚拟机的关键约定、Java的跨平台性、自动垃圾回收机制、源文件结构、包的概念及作用等多个核心知识点,旨在帮助学生更好地准备Java期末考试。 ...
[详细]
蜡笔小新 2024-12-19 19:00:10
-
KMP算法是一种高效的字符串模式匹配算法,能够在不进行回溯的情况下完成匹配,其时间复杂度为O(m+n),其中m和n分别为文本串和模式串的长度。本文将详细介绍KMP算法的工作原理,并提供C语言实现。 ...
[详细]
蜡笔小新 2024-12-19 15:49:59
-
mobiledu2502873617
这个家伙很懒,什么也没留下!