热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Java实现优先队列:二叉堆详解

本文详细介绍了二叉堆的概念及其在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
推荐阅读
  • 从 .NET 转 Java 的自学之路:IO 流基础篇
    本文详细介绍了 Java 中的 IO 流,包括字节流和字符流的基本概念及其操作方式。探讨了如何处理不同类型的文件数据,并结合编码机制确保字符数据的正确读写。同时,文中还涵盖了装饰设计模式的应用,以及多种常见的 IO 操作实例。 ... [详细]
  • Java 类成员初始化顺序与数组创建
    本文探讨了Java中类成员的初始化顺序、静态引入、可变参数以及finalize方法的应用。通过具体的代码示例,详细解释了这些概念及其在实际编程中的使用。 ... [详细]
  • 本文介绍了Java并发库中的阻塞队列(BlockingQueue)及其典型应用场景。通过具体实例,展示了如何利用LinkedBlockingQueue实现线程间高效、安全的数据传递,并结合线程池和原子类优化性能。 ... [详细]
  • 主要用了2个类来实现的,话不多说,直接看运行结果,然后在奉上源代码1.Index.javaimportjava.awt.Color;im ... [详细]
  • 本文详细介绍了 Apache Jena 库中的 Txn.executeWrite 方法,通过多个实际代码示例展示了其在不同场景下的应用,帮助开发者更好地理解和使用该方法。 ... [详细]
  • 本文介绍了如何通过 Maven 依赖引入 SQLiteJDBC 和 HikariCP 包,从而在 Java 应用中高效地连接和操作 SQLite 数据库。文章提供了详细的代码示例,并解释了每个步骤的实现细节。 ... [详细]
  • golang常用库:配置文件解析库/管理工具viper使用
    golang常用库:配置文件解析库管理工具-viper使用-一、viper简介viper配置管理解析库,是由大神SteveFrancia开发,他在google领导着golang的 ... [详细]
  • 本文详细介绍如何使用Python进行配置文件的读写操作,涵盖常见的配置文件格式(如INI、JSON、TOML和YAML),并提供具体的代码示例。 ... [详细]
  • C++实现经典排序算法
    本文详细介绍了七种经典的排序算法及其性能分析。每种算法的平均、最坏和最好情况的时间复杂度、辅助空间需求以及稳定性都被列出,帮助读者全面了解这些排序方法的特点。 ... [详细]
  • 本文详细介绍了Akka中的BackoffSupervisor机制,探讨其在处理持久化失败和Actor重启时的应用。通过具体示例,展示了如何配置和使用BackoffSupervisor以实现更细粒度的异常处理。 ... [详细]
  • UNP 第9章:主机名与地址转换
    本章探讨了用于在主机名和数值地址之间进行转换的函数,如gethostbyname和gethostbyaddr。此外,还介绍了getservbyname和getservbyport函数,用于在服务器名和端口号之间进行转换。 ... [详细]
  • 本文详细解析了Python中的os和sys模块,介绍了它们的功能、常用方法及其在实际编程中的应用。 ... [详细]
  • 本文探讨了 Objective-C 中的一些重要语法特性,包括 goto 语句、块(block)的使用、访问修饰符以及属性管理等。通过实例代码和详细解释,帮助开发者更好地理解和应用这些特性。 ... [详细]
  • 本文详细介绍了Java中org.w3c.dom.Text类的splitText()方法,通过多个代码示例展示了其实际应用。该方法用于将文本节点在指定位置拆分为两个节点,并保持在文档树中。 ... [详细]
  • 本文详细探讨了VxWorks操作系统中双向链表和环形缓冲区的实现原理及使用方法,通过具体示例代码加深理解。 ... [详细]
author-avatar
mobiledu2502873617
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有