本文主要讲的是实现最大堆,最小堆其实直接可以通过最大堆对比较运算做一定的处理,即可以得到。
同时可以参考 Java PriorityQueue 的实现。

堆可以作为优先队列。时间复杂度 O(logn)

为什么使用堆?堆的 API 定义2.添加元素与 siftUpreplaceheapify为什么 heapify 操作会更快?siftDown 的操作更多问题1.D叉堆2.索引堆完整的堆的代码

为什么使用堆?

不管是 普通线性结构 还是 顺序线性结构。要么入队 O(1) 出队 O(n),要么 入队 O(n) 出队 O(1)。

堆的入队和出队的时间复杂度都是 O(logn),性能非常好。

堆的 API 定义

public class MaxHeap<E extends Comparable<E>> {    private Array data;public MaxHeap(int capacity) {    }public MaxHeap(E[] arr) {    }// 返回堆中的元素个数public int size() {    }// 返回一个布尔值, 表示堆中是否为空public boolean isEmpty() {    }// 向堆中添加元素public void add(E e) {    }// 看堆中的最大元素public E findMax() {    }// 取出堆中最大元素public E extractMax() {    }/**     * 取出堆中最大元素,并替换成 e     * O(logn)     */public E replace(E e) {    }public void heapify() {    }}

2.添加元素与 siftUp

将元素添加到 array 的末尾,然后需要判断当前节点与其父节点的大小,如果它大于它的父节点,那么需要交换他们。
交换完成之后,需要继续将其父节点与其父亲的父亲节点继续比较,这就是一个 siftUp 的过程。

// 向堆中添加元素public void add(E e) {   data.addLast(e);   siftUp(data.getSize() - 1);}

private void siftUp(int k) {   //位置交换.不断上浮   while (k > 0 && data.get(parent(k)).compareTo(data.get(k)) 0) {       data.swap(k, parent(k));       k = parent(k);   }}//性能更好的siftUpprivate void siftUp2(int k, E x) {  E target = data.get(k);  while (k > 0) {     int parent = parent(k);     E e = data.get(parent);     if (e.compareTo(target) >= 0) break;         data.set(k, e);         k = parent(k);  }  data.set(k, target);}

replace

取出堆中的最大元素之后,再放入一个新的元素。操作方法:
1.将堆顶的元素替换成目标元素,添加完新的元素后可能会违背堆的性质。
2.使用 siftDown() 满足堆的性质。

public E replace(E e) {   E ret = findMax();   data.set(0, e);   siftDown(0);   return ret;}// 看堆中的最大元素public E findMax() {   if (data.getSize() == 0)      throw new IllegalArgumentException("Can not findMax when heap is empty.");

   return data.get(0);}

操作步骤:

  • 1.获取到堆顶元素

  • 2.将堆顶元素设置成目标元素

  • 3.因为可能违背了堆的性质,于是调用 siftDown(0) 进行下沉操作。

heapify

what is heapify ?

将任意的一个数组整理成堆的形状.


首先把一个数组看成是一个完全二叉树,然后找到倒数第一个非叶子点,然后倒着从尾向前一个一个 siftDown 就可以了。

如何获取到数组中的第一个非叶子节点?
找到最后一个节点的索引,然后拿到它的父节点的 index 即可。


22 便是第一个非叶子节点,我们从它开始进行下沉操作。

public MaxHeap(E[] arr) {    data = new Array<>(arr);    //找到倒数第一个非叶子节点, 然后倒着往回 siftDown(i)    for (int i = parent(arr.length - 1); i >= 0; i--) {        siftDown(i);    }}

为什么 heapify 操作会更快?

如果直接一个元素一个元素的添加进堆的,需要添加的元素为n。
如果我们直接抛弃叶子节点,直接从它的上一层开始进行heapify,因为堆是一棵完全二叉树,使用 heapify 直接可以抛掉一半的节点。

复杂度分析:

  • 将n个元素逐个插入到一个空堆中,算法复杂度为 O(nlogn)

  • heapify 的算法复杂度是 O(n) 的。

siftDown 的操作

上面我们提到的两个操作,replaceheapify 都需要调用 siftDown 来进行下沉操作的。

private void siftDown(int k) {    while (leftChild(k)         int j = leftChild(k);        //目的是找到更大的一个孩子        //这行的逻辑是判断当前父节点是否有右孩子,同时这个右孩子比左孩子更多        if (j + 1 1).compareTo(data.get(j)) > 0) {            j++;        }        //父节点比最大的孩子节点大        if (data.get(k).compareTo(data.get(j)) >= 0) {            break;        }        data.swap(k, j);        //父子交换,继续下沉        k = j;    }}

siftDown 的逻辑其实就是将当前节点进行下沉,步骤如下:

  • 1.首先找到 siftDown 的孩子,先找它的左孩子,如果左孩子存在,再找其有没有右孩子,如果两个孩子都有,那么取其中更大的一个来和目标节点比较。如果 leftChild(k) < data.getSize() 则表示当前节点没有孩子节点了,所以它已经属于最深的一层了,不需要继续下沉了。

  • 2.找到了目标节点更大的一个孩子节点之后,和它进行对比,如果它比孩子节点更大,则直接退出,不需要继续下沉了。如果它比孩子节点小,则需要进行交换。

  • 3.交换完成之后,将 k 指向 孩子节点的index j,在下一步继续对 j 进行下沉操作。

效率更高的下沉操作:

private void siftDown2(int k, E x) {    int half = size() >>> 1;

    while (k         int child = leftChild(k);        E c = data.get(child);        int right = child + 1;

        if (right  0) {            c = data.get(child = right);        }

        if (x.compareTo(c) >= 0) {            break;        }        k = child;    }

    data.set(k, x);}

更多问题

1.D叉堆

D叉堆

对于每一个节点来说,它有d个节点,然后组成完全d叉树。这种堆的层数是更低的,它的时间复杂度就是 O(logdN)。

2.索引堆

上文我们实现的堆只能看到堆顶的元素,却不能看到堆中间的元素的。
在很多情况下,我们需要看到堆中的元素,并对其进行修改,因此我们可以构造出一个索引堆。

完整的堆的代码

本文实现的最大堆的完整代码:

https://github.com/leihuazhe/zane-sf/blob/master/src/main/java/com/zane/sf/heap/MaxHeap.java

dom4j实现为list添加父节点_最大堆的实现与原理相关推荐

  1. dom4j实现为list添加父节点_Heap 堆的实现

    堆(数据结构) 什么是堆 堆(Heap)是计算机科学中一类特殊的数据结构的统称.堆通常是一个可以被看做一棵完全二叉树的数组对象 堆的性质 这种用数组实现的二叉树,假设节点的索引值为index,那么: ...

  2. ztree 更新配置后重新渲染树_【问】zTree异步加载时添加父节点怎样避免再次加载整树...

    该楼层疑似违规已被系统折叠 隐藏此楼查看此楼 具体情况是 每当我执行add方法添加父节点的时候,都回去server端重新获取一次树节点信息,并加载在新增加的节点下. 页面代码如下 var settin ...

  3. sql server父节点_将新节点添加到现有SQL Server Always On可用性组中

    sql server父节点 This is the 5th article in the series of a comprehensive guide to SQL Server Always On ...

  4. qteewidgetitem添加子节点_行为树的节点

    一,行为树几大节点: Root节点:只能有一个子节点,并且该节点必须是" 复合"节点.不能将任何Decorator或Service附加到Root,在root可以指定其黑板资源: T ...

  5. easyui的tree获取父节点_力扣 1519——子数中标签相同的节点数

    本题主要在于对树这种数据结构的考察,以及深度优先遍历的使用,优化时可以采取空间换时间的策略. 原题 给你一棵树(即,一个连通的无环无向图),这棵树由编号从 0 到 n - 1 的 n 个节点组成,且恰 ...

  6. vba xml 怎么设置父节点_熊二做了一个xml报文处理的开源库easyxml

    ❝ 自信.冷静.专注.-- TM 熊的自我勉励 ❞ 1. 前言 熊二从去年开始,因项目需求接触到xml报文的处理,也是我第一次学习用C/C++的方式处理基于DOM模型的xml报文.因为本人比较懒hhh ...

  7. unity查找子物体、子节点、获取子节点对象脚本,添加子节点脚本,添加父节点脚本

    分享一个代码管理片段的代码,主要是用于查找子物体,控制子物体等等的. 静态代码片段,便于调用. 不做太多的解释,直接上代码,每个方法都有注释. 有任何问题直接留言,看到会回复 QQ群 20701909 ...

  8. layui如何获取父节点的父节点_区块链如何运用merkle tree验证交易真实性

    大部分材料都会提到区块中保存了merkle根,并且利用它作交易真实性验证.但是具体如何作这个真实性验证,没有一篇文章可以通俗的讲出来.本文假设你已经知道区块链中merkle tree的原理,现在想搞明 ...

  9. java递归查询父节点_父节点递归查询和子节点递归查询函数

    本文为博主原创,未经允许不得转载: 由于在项目中用到了向上查询父节点的递归查询和向下查询子节点查询的递归查询,由于在实现过程中,写递归查询的sql函数 花费了较长的时间,所以在此总结一下,两种递归查询 ...

最新文章

  1. 绘制电路图风格的纠结
  2. LevelDb简单介绍和原理——本质:类似nedb,插入数据文件不断增长(快照),再通过删除老数据做更新...
  3. php偷取,PHP偷取UTF-8目标网页内容输出为空白
  4. Facebook Architecture @ QCon Next Month: Infrastructure, HTML5, NoSQL, OO Design
  5. 教你几招识别和防御Web网页木马
  6. 2020.2idea创建web_使用IDEA配置Tomcat(亲测成功)
  7. [leetcode]1.两数之和
  8. SQL数据库语言基础之SqlServer系统函数、聚合集合函数【大总结】
  9. Ubuntu已经很好用了
  10. Ubuntu14.04+RabbitMQ3.6.3+Golang的最佳实践
  11. mysql 逻辑备份工具_MySQL逻辑备份工具-mydumper
  12. 【BZOJ28323874】宅男小C [模拟退火][贪心]
  13. Atitit .html5刮刮卡的gui实现总结
  14. c语言关于内存编程,c语言内存
  15. python求三位数每一位的和_输入一个三位数的整数,求这个三位数每一位上数字的和是多少。例如,输入: 382,输出:和为 13。 编写 Python 程序实现上述要求...
  16. office插件开发_PPT插件——OK之安装篇
  17. 题解 CF32C 【Flea】
  18. P1419 寻找段落
  19. Linux系统安装docker教程
  20. 【Tensorflow】 tf.equal(tf.argmax(y, 1),tf.argmax(y_, 1))用法

热门文章

  1. 【Android】魅族Flyme OS 3摄像头无法预览的问题
  2. 电脑程序无法打开,开机5S后速度变慢,程序无法安装
  3. 分发服务器linux,Linux-LVS分发服务器
  4. yapi 权限_win10 x64下从0开始搭建YApi可视化接口管理平台
  5. 集合竞价如何买入_世界上最稳健的抓涨停方法“10分钟集合竞价”选股诀窍,买入直接稳赚10个点,赚到笑...
  6. 21day学通python_python21day
  7. swift int转string_用Swift开发macOS程序,九、目录模块
  8. Python操作数据库完成接口测试
  9. 案例 自动办公_国浩分享 | 非诉讼律师办公神器盘点
  10. mqtt实例 php_php搭建MQtt协议服务