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

堆(Heap)详解——Java实现

Heap堆定义:(这里只讲二叉堆)堆实为二叉树的一种,分为最小堆和最大堆,具有以下性质:任意节点小于大于它的所有后裔,最小大元在堆的根上。堆总是一棵完全二叉树将根节

Heap

堆定义:(这里只讲二叉堆)堆实为二叉树的一种,分为最小堆和最大堆,具有以下性质:

  • 任意节点小于/大于它的所有后裔,最小/大元在堆的根上。
  • 堆总是一棵完全二叉树

  将根节点最大的堆叫做最大堆或大根堆,根节点最小的堆叫做最小堆或小根堆

堆的相关操作:

  • 建立
  • 插入
  • 删除

应用:

  • 堆排序
  • 优先队列
  • 合并容器元素
  • 找出第k大元素

 

Java实现:

/** * Created by XuTao on 2018/11/5 22:10 * ···最小堆··· * 《注意: 实际上并不需要用节点来真正构造一颗树,我们只是在数组中操作排序,调整好的数组就是一个堆的层遍历结果》 * * 插入: * 也是插入末尾,然后调整,调整也应该是一个连续向上的过程,建树就是一个连续插入的过程 * * 删除最小: * 即删除root: * 用末尾一个代替root,删除末尾,然后siftDown,如果子节点有更小的,每次只需要找到最小的子节点,然后交换即可。 * * siftDown: * 如果建树得以保证,那么如果子节点有更小的,每次只需要找到最小的子节点,然后交换即可。 * 如果是一个乱的树,那么就要考虑,比较麻烦, 解决方式: * i 从 最后一个节点的父节点出开始迭代,直到i = 0; * 每次检查时,将大的节点交换到末尾——就是要到底,如果大,就要成为叶节点,不是只交换一次(用循环) * * 那么就有两种构建方法: * 1.乱序构建,调整( 一个一个添加(从数组中),添加到最后一个,树的最右最下方的那个,然后siftUp,从下往上调整就可以了 ,O(log2(n))) * 2.一次一节点,依次调整 * * 

* 思考题: 设计算法检查一个完全二叉树是不是堆,是的话是最大堆还是最小堆。 * 思路:元素1个,同为最大最小堆 * 元素>1个: * 判断第一二个大小 * 第一个大: 可能为最大堆,然后递归校验,如果每一个节点都比子节点大,那么是最大堆,否则不是堆 * 第二个大: 可能为最小堆,然后递归校验,如果每一个节点都比子节点小,那么是最小堆,否则不是堆 *

* *时间复杂度分析: * 建树:两种方式都是 O(nlog2(n)) * 插入: O(log2(n)) * 删除: O(log2(n)) */ public class Heap { private int[] data; private final int maxSize = 128; //预设大小,足够就行 private int heapSize; //实际大小 public Heap(int[] input) { data = new int[maxSize]; heapSize = input.length; for (int i = 0; i //这个地方其实并不好,只是将传入的数组读入我的数组中,一方有不断插入操作,如果没有插入操作则不必要; data[i] = input[i]; } } public void build_1() { /** * 建树方法1: * 每次插入一个节点 */ int a = heapSize; heapSize = 0; for (int i = 0; i ) { insert(data[i]); } } public void build_2() { /** * 建树方法2: * 以原来的乱序进行调整:siftDown */ if (heapSize <= 1) return; for (int i = getParent(heapSize - 1); i >= 0; i--) { // 从末元素的父节点开始,一次一次进行siftDown siftDown(i); } } /** * 由上而下调整, sift——筛 * @param start */ public void siftDown(int start) { //start至少1子,不用担心溢出问题 while (getLeft(start) //注意,这里必须是小于,不能等于,如果该节点的左节点是末尾节点则结束,条件是getLeft(start)==heapSize-1 int min = 0;//判别有没有发生交换的条件 //无右子 if (getRight(start) >= heapSize) { if (data[start] > data[getLeft(start)]) { min = getLeft(start); swap(start, min); } } //2子 else { min = data[getLeft(start)] > data[getRight(start)] ? getRight(start) : getLeft(start); if (data[start] > data[min]) { swap(start, min); } } if (min == 0) break;//满足堆条件,退出 start = min; //不满足堆条件,还可以调整,继续循环 } } /** * 由下而上调整 * @param start 开始的下标 */ public void siftUp(int start) { if (start <= 0) return; while (data[start] //一直发生交换,直到满足条件 swap(start, getParent(start)); start = getParent(start); if (start <= 0) break;// root } } public void insert(int a) { /** * 插入的话会使数组长度加一,比较麻烦,于是我建立一个比较大的树,用一个较大的量maxSize来限定堆的最大容量,用heapSize来声明实际的容量 */ data[heapSize] = a; siftUp(heapSize); heapSize++; } public int getLeft(int i) { return 2 * i + 1; } public int getRight(int i) { return 2 * i + 2; } public int getParent(int i) { if (i == 0) return -1; return (i - 1) >> 1; //除以2 } public void swap(int i, int j) { int temp = data[i]; data[i] = data[j]; data[j] = temp; } public void display() { for (int i = 0; i ) { System.out.print(data[i] + " "); } System.out.println(); } public static void main(String[] args) { int[] a = new int[]{8, 12, 2, 5, 3, 7, -1, 44, 23}; Heap heap = new Heap(a); heap.display(); // heap.build_1(); heap.build_2(); heap.insert(-4); heap.display(); } }

 


推荐阅读
  • Ihavethefollowingonhtml我在html上有以下内容<html><head><scriptsrc..3003_Tes ... [详细]
  • Java太阳系小游戏分析和源码详解
    本文介绍了一个基于Java的太阳系小游戏的分析和源码详解。通过对面向对象的知识的学习和实践,作者实现了太阳系各行星绕太阳转的效果。文章详细介绍了游戏的设计思路和源码结构,包括工具类、常量、图片加载、面板等。通过这个小游戏的制作,读者可以巩固和应用所学的知识,如类的继承、方法的重载与重写、多态和封装等。 ... [详细]
  • Iamtryingtomakeaclassthatwillreadatextfileofnamesintoanarray,thenreturnthatarra ... [详细]
  • Java容器中的compareto方法排序原理解析
    本文从源码解析Java容器中的compareto方法的排序原理,讲解了在使用数组存储数据时的限制以及存储效率的问题。同时提到了Redis的五大数据结构和list、set等知识点,回忆了作者大学时代的Java学习经历。文章以作者做的思维导图作为目录,展示了整个讲解过程。 ... [详细]
  • 本文介绍了如何在给定的有序字符序列中插入新字符,并保持序列的有序性。通过示例代码演示了插入过程,以及插入后的字符序列。 ... [详细]
  • JavaSE笔试题-接口、抽象类、多态等问题解答
    本文解答了JavaSE笔试题中关于接口、抽象类、多态等问题。包括Math类的取整数方法、接口是否可继承、抽象类是否可实现接口、抽象类是否可继承具体类、抽象类中是否可以有静态main方法等问题。同时介绍了面向对象的特征,以及Java中实现多态的机制。 ... [详细]
  • 本文讨论了一个关于cuowu类的问题,作者在使用cuowu类时遇到了错误提示和使用AdjustmentListener的问题。文章提供了16个解决方案,并给出了两个可能导致错误的原因。 ... [详细]
  • 个人学习使用:谨慎参考1Client类importcom.thoughtworks.gauge.Step;importcom.thoughtworks.gauge.T ... [详细]
  • 本文详细介绍了Java中vector的使用方法和相关知识,包括vector类的功能、构造方法和使用注意事项。通过使用vector类,可以方便地实现动态数组的功能,并且可以随意插入不同类型的对象,进行查找、插入和删除操作。这篇文章对于需要频繁进行查找、插入和删除操作的情况下,使用vector类是一个很好的选择。 ... [详细]
  • 猜字母游戏
    猜字母游戏猜字母游戏——设计数据结构猜字母游戏——设计程序结构猜字母游戏——实现字母生成方法猜字母游戏——实现字母检测方法猜字母游戏——实现主方法1猜字母游戏——设计数据结构1.1 ... [详细]
  • [大整数乘法] java代码实现
    本文介绍了使用java代码实现大整数乘法的过程,同时也涉及到大整数加法和大整数减法的计算方法。通过分治算法来提高计算效率,并对算法的时间复杂度进行了研究。详细代码实现请参考文章链接。 ... [详细]
  • 学习Java异常处理之throws之抛出并捕获异常(9)
    任务描述本关任务:在main方法之外创建任意一个方法接收给定的两个字符串,把第二个字符串的长度减1生成一个整数值,输出第一个字符串长度是 ... [详细]
  • 本文介绍了Swing组件的用法,重点讲解了图标接口的定义和创建方法。图标接口用来将图标与各种组件相关联,可以是简单的绘画或使用磁盘上的GIF格式图像。文章详细介绍了图标接口的属性和绘制方法,并给出了一个菱形图标的实现示例。该示例可以配置图标的尺寸、颜色和填充状态。 ... [详细]
  • 本文介绍了Java中Hashtable的clear()方法,该方法用于清除和移除指定Hashtable中的所有键。通过示例程序演示了clear()方法的使用。 ... [详细]
  • 本文介绍了在使用Laravel和sqlsrv连接到SQL Server 2016时,如何在插入查询中使用输出子句,并返回所需的值。同时讨论了使用CreatedOn字段返回最近创建的行的解决方法以及使用Eloquent模型创建后,值正确插入数据库但没有返回uniqueidentifier字段的问题。最后给出了一个示例代码。 ... [详细]
author-avatar
布拉格mi_716
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有