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

二叉树之堆

TopK问题现实中我们通常把堆(一种二叉树)使用顺序结构的数组来存储,需要注意的是这里的堆和操作系统虚拟进程地址空间中的堆是两回事,一个是数据结构&#x

TopK问题
在这里插入图片描述
现实中我们通常把堆(一种二叉树)使用顺序结构的数组来存储,需要注意的是这里的堆和操作系统虚拟进程地址空间中的堆是两回事,一个是数据结构,一个是操作系统中管理内存的一块区域分段。

1 、堆的概念


  • 堆中某个节点的值总是不大于或不小于其父节点的值;
  • 堆总是一棵完全二叉树。
  • 按照层序遍历的结果储存在数组中
  • 堆的基本作用是快速找集合中的最值
    在这里插入图片描述

2、堆的实现


2.1堆向下调整建大堆

前提:左右子树必须已经是一个堆,才能调整,即只有一个不满足堆的性质

//向下调整建大堆private static void CreateBig(int[] arr, int index) {//index就是要调整的parentint max &#61; index * 2 &#43; 1;/*** 保证有右孩子&#xff0c;再比较* 不是叶子结点&#xff0c;一定有左孩子&#xff0c;但不一定有右孩子* (1)没有右孩子&#xff0c;左边大* (2)有右孩子&#xff0c;左边大或者右边大* *///当有左孩子时执行以下while (max < arr.length) {//如果有右孩子&#xff0c;并且右孩子大于左孩子&#xff0c;改变max的值if (max &#43; 1 < arr.length && arr[max &#43; 1] > arr[max]) {max&#43;&#43;;}//判断交换调整//当parent小于孩子时交换&#xff0c;否则不执行if (arr[index] < arr[max]) {int t &#61; arr[max];arr[max] &#61; arr[index];arr[index] &#61; t;//改变index的max的值&#xff0c;使循环继续index &#61; max;max &#61; 2 * index &#43; 1;} else {break;}}}

2.2堆向下调整建小堆

//向下调整&#xff0c;建小堆private static void CreateSmall(int[] arr, int index) {//左孩子下标&#xff0c;代表 index 的最小值孩子的下标int min &#61; index * 2 &#43; 1;//1.有叶子结点while (min < arr.length) {//2.保证有右孩子的前提下&#xff0c;右孩子比较小//其他情况min就是最小的&#xff0c;即左孩子小if (min &#43; 1 < arr.length && arr[min &#43; 1] < arr[min]) {min&#43;&#43;;}//3.根小于该孩子结点则不执行if (arr[index] <&#61; arr[min]) {break;}//4.否则的话交换int t &#61; arr[min];arr[min] &#61; arr[index];arr[index] &#61; t;//以该结点为根&#xff0c;继续向下调整index &#61; min;min &#61; 2 * min &#43; 1;}}

2.3向上调整建大堆

/* 向上调整为大堆 */&#64;Overridepublic void adjustUp(int[] array, int index) { //index为插入的下标//父亲结点int parent &#61; (index - 1) / 2;//一直调整到堆顶while (parent >&#61; 0) {if (item[index] > item[parent]) {int t &#61; item[index];item[index] &#61; item[parent];item[parent] &#61; t;index &#61; parent;parent &#61; (index - 1) / 2;} else {break;}}}

2.4建堆

给出一个数组&#xff0c;逻辑上是一个二叉树&#xff0c;但是还不是堆&#xff0c;从倒数的第一个非叶子节点的子树开始调整&#xff0c;一直调整到根节点的树&#xff0c;就可以调整成堆。
时间复杂度为O(N*logN)&#xff0c;实际是O&#xff08;N&#xff09;

private static void CreateHeap(int[] arr) {//从最后一个非叶子结点的下标开始for (int i &#61; (arr.length - 1 - 1) / 2; i >&#61; 0; i--) {//不断地做向下调整//CreateSmall(arr, i);CreateBig(arr,i);}}

2.3堆的插入、查看、删除

public class IHeapImpl implements IHeap {private int[] item;private int userSize;public IHeapImpl(int[] arr) {this.item &#61;Arrays.copyOf(arr,arr.length);// 代表数组中被视为堆数据的个数this.userSize &#61; 0;}/* 1.堆的插入 */ //先插入一个数字到数组的尾上&#xff0c;长度&#43;&#43;&#xff0c;再进行向上调整算法&#xff0c;直到满足堆。&#64;Overridepublic void add(int item) {if (Full()) {this.item &#61; Arrays.copyOf(this.item, 2 * this.item.length &#43; 1);}this.item[userSize] &#61; item;this.userSize&#43;&#43;;//加到最后一个了&#xff0c;所以要向上调整adjustUp(this.item, this.userSize - 1);}/* 向上调整为大堆 */&#64;Overridepublic void adjustUp(int[] array, int index) { //index为插入的下标//父亲结点int parent &#61; (index - 1) / 2;//一直调整到堆顶while (parent >&#61; 0) {if (item[index] > item[parent]) {int t &#61; item[index];item[index] &#61; item[parent];item[parent] &#61; t;index &#61; parent;parent &#61; (index - 1) / 2;} else {break;}}}&#64;Overridepublic int peek() {if (isEmpty()) {//不支持的异常throw new UnsupportedOperationException("堆为空");}return this.item[0];}//返回堆顶元素&#xff0c;并该数据元素//删除堆是删除堆顶的数据&#xff0c;将堆顶的数据根和最后一个数据一换&#xff0c;然后删除数组最后一个数据&#xff0c;再进行向下调整算法。&#64;Overridepublic int pop() {if (isEmpty()) {//抛出不支持的异常throw new UnsupportedOperationException("堆为空");}//先保留堆顶元素int old&#61;this.item[0];//先交换&#xff0c;把堆顶元素和最后一个元素交换,最后一个元素下标为usedSize-1int t &#61; this.item[0];this.item[0] &#61; this.item[this.userSize - 1];this.item[this.userSize - 1] &#61; t;//完了之后将交换后的最后一个元素删除&#xff0c;只要userSize--&#xff0c;说明下次做向下调整的时候就不走这里了this.userSize--;//此时只有0位值不满足堆的性质&#xff0c;向下调整建大堆adjustDown(this.userSize , 0);return old;}//向下调整&#64;Overridepublic void adjustDown(int len, int index) {int max &#61; index * 2 &#43; 1;while (max < len) {if (max &#43; 1 < len && this.item[max] < this.item[max &#43; 1]) {max&#43;&#43;;}if (item[index] >&#61; item[max]) {break;}int t &#61; item[index];item[index] &#61; item[max];item[max] &#61; t;index &#61; max;max &#61; index * 2 &#43; 1;}}//创建大根堆&#64;Overridepublic void createHeap(int[] array) {this.userSize&#61;array.length;for (int i &#61; (array.length - 1 - 1) / 2; i >&#61; 0; i--) {adjustDown(this.userSize, i);}}&#64;Overridepublic void show() {for (int i &#61; 0; i < this.userSize; i&#43;&#43;) {System.out.print(item[i] &#43; " ");}}&#64;Overridepublic boolean Full() {return this.userSize &#61;&#61; this.item.length;}&#64;Overridepublic boolean isEmpty() {return this.userSize &#61;&#61; 0;}
}


推荐阅读
  • AIX编程挑战赛:AIX正方形问题的算法解析与Java代码实现
    在昨晚的阅读中,我注意到了CSDN博主西部阿呆-小草屋发表的一篇文章《AIX程序设计大赛——AIX正方形问题》。该文详细阐述了AIX正方形问题的背景,并提供了一种基于Java语言的解决方案。本文将深入解析这一算法的核心思想,并展示具体的Java代码实现,旨在为参赛者和编程爱好者提供有价值的参考。 ... [详细]
  • 字节流(InputStream和OutputStream),字节流读写文件,字节流的缓冲区,字节缓冲流
    字节流抽象类InputStream和OutputStream是字节流的顶级父类所有的字节输入流都继承自InputStream,所有的输出流都继承子OutputStreamInput ... [详细]
  • Ihavetwomethodsofgeneratingmdistinctrandomnumbersintherange[0..n-1]我有两种方法在范围[0.n-1]中生 ... [详细]
  • 在多线程并发环境中,普通变量的操作往往是线程不安全的。本文通过一个简单的例子,展示了如何使用 AtomicInteger 类及其核心的 CAS 无锁算法来保证线程安全。 ... [详细]
  • 如何在Java中使用DButils类
    这期内容当中小编将会给大家带来有关如何在Java中使用DButils类,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。D ... [详细]
  • 本文详细介绍了 PHP 中对象的生命周期、内存管理和魔术方法的使用,包括对象的自动销毁、析构函数的作用以及各种魔术方法的具体应用场景。 ... [详细]
  • 本文深入探讨了Java多线程环境下的同步机制及其应用,重点介绍了`synchronized`关键字的使用方法和原理。`synchronized`关键字主要用于确保多个线程在访问共享资源时的互斥性和原子性。通过具体示例,如在一个类中使用`synchronized`修饰方法,展示了如何实现线程安全的代码块。此外,文章还讨论了`ReentrantLock`等其他同步工具的优缺点,并提供了实际应用场景中的最佳实践。 ... [详细]
  • 本文探讨了利用JavaScript实现集合的对称差集算法的方法。该算法旨在处理多个数组作为输入参数,同时保留每个数组中元素的原始顺序。算法不会移除单个数组内的重复元素,但会删除在不同数组之间出现的重复项。通过这种方式,能够有效地计算出多个数组的对称差集。 ... [详细]
  • 在前文探讨了Spring如何为特定的bean选择合适的通知器后,本文将进一步深入分析Spring AOP框架中代理对象的生成机制。具体而言,我们将详细解析如何通过代理技术将通知器(Advisor)中包含的通知(Advice)应用到目标bean上,以实现切面编程的核心功能。 ... [详细]
  • C++ 进阶:类的内存布局与虚函数类的实现细节
    C++ 进阶:类的内存布局与虚函数类的实现细节 ... [详细]
  • 本文详细介绍了在CodeUp平台中实现大数进制转换的技术方法。具体而言,该问题要求将一个最多包含30位数字的十进制非负整数转换为二进制表示。输入数据包含多行,每行包含一个不超过30位的十进制非负整数。通过高效的算法设计,确保了大数转换的准确性和性能。 ... [详细]
  • 在 Linux 环境下,多线程编程是实现高效并发处理的重要技术。本文通过具体的实战案例,详细分析了多线程编程的关键技术和常见问题。文章首先介绍了多线程的基本概念和创建方法,然后通过实例代码展示了如何使用 pthreads 库进行线程同步和通信。此外,还探讨了多线程程序中的性能优化技巧和调试方法,为开发者提供了宝贵的实践经验。 ... [详细]
  • 在Linux系统中,通过使用`read`和`write`函数可以实现文件的高效复制操作。`open`函数用于打开或创建文件,其返回值为文件描述符,成功时返回一个有效的文件描述符,失败时返回-1。`path`参数指定了要操作的文件路径,而`oflag`参数则定义了文件的打开模式和属性。此外,为了确保数据的完整性和一致性,还需要合理处理文件读取和写入过程中的错误和异常情况。 ... [详细]
  • 蓝桥杯物联网基础教程:通过GPIO输入控制LED5的点亮与熄灭
    本教程详细介绍了如何利用STM32的GPIO接口通过输入信号控制LED5的点亮与熄灭。内容涵盖GPIO的基本配置、按键检测及LED驱动方法,适合具有STM32基础的读者学习和实践。 ... [详细]
  • 在托管C++中开发应用程序时,遇到了如何声明和操作字符串数组的问题。本文详细探讨了字符串数组在托管C++中的应用与实现方法,包括声明、初始化、遍历和常见操作技巧,为开发者提供了实用的参考和指导。 ... [详细]
author-avatar
小Q理性的激情农_885
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有