在Java数据结构和算法(五)——队列中我们介绍了优先级队列,优先级队列是一种抽象数据类型(ADT),它提供了删除最大(或最小)关键字值的数据项的方法,插入数据项的方法,优先级队列可以用有序数组来实现,这种实现方式尽管删除最大数据项的时间复杂度为O(1),但是插入还是需要较长的时间 O(N),因为每次插入平均需要移动一半的数据项,来保证插入后,数组依旧有序。

  本篇博客我们介绍另外一种数据结构——堆,注意这里的堆和我们Java语言,C++语言等编程语言在内存中的“堆”是不一样的,这里的堆是一种树,由它实现的优先级队列的插入和删除的时间复杂度都为O(logN),这样尽管删除的时间变慢了,但是插入的时间快了很多,当速度非常重要,而且有很多插入操作时,可以选择用堆来实现优先级队列。

1、堆的定义

  ①、它是完全二叉树,除了树的最后一层节点不需要是满的,其它的每一层从左到右都是满的。注意下面两种情况,第二种最后一层从左到右中间有断隔,那么也是不完全二叉树。

  

  ②、它通常用数组来实现。

  

  这种用数组实现的二叉树,假设节点的索引值为index,那么:

  节点的左子节点是 2*index+1,

  节点的右子节点是 2*index+2,

  节点的父节点是 (index-1)/2。

  ③、堆中的每一个节点的关键字都大于(或等于)这个节点的子节点的关键字。

  这里要注意堆和前面说的二叉搜索树的区别,二叉搜索树中所有节点的左子节点关键字都小于右子节点关键字,在二叉搜索树中通过一个简单的算法就可以按序遍历节点。但是在堆中,按序遍历节点是很困难的,如上图所示,堆只有沿着从根节点到叶子节点的每一条路径是降序排列的,指定节点的左边节点或者右边节点,以及上层节点或者下层节点由于不在同一条路径上,他们的关键字可能比指定节点大或者小。所以相对于二叉搜索树,堆是弱序的。

2、遍历和查找

  前面我们说了,堆是弱序的,所以想要遍历堆是很困难的,基本上,堆是不支持遍历的。

  对于查找,由于堆的特性,在查找的过程中,没有足够的信息来决定选择通过节点的两个子节点中的哪一个来选择走向下一层,所以也很难在堆中查找到某个关键字。

  因此,堆这种组织似乎非常接近无序,不过,对于快速的移除最大(或最小)节点,也就是根节点,以及能快速插入新的节点,这两个操作就足够了。

3、移除

  移除是指删除关键字最大的节点(或最小),也就是根节点。

  根节点在数组中的索引总是0,即maxNode = heapArray[0];

  移除根节点之后,那树就空了一个根节点,也就是数组有了一个空的数据单元,这个空单元我们必须填上。

  第一种方法:将数组所有数据项都向前移动一个单元,这比较费时。

  第二种方法:

    ①、移走根

    ②、把最后一个节点移动到根的位置

    ③、一直向下筛选这个节点,直到它在一个大于它的节点之下,小于它的节点之上为止。

  具体步骤如下:

  

  图a表示把最后一个节点移到根节点,图b、c、d表示将节点向下筛选到合适的位置,它的合适位置在最底层(有时候可能在中间),图e表示节点在正确位置的情景。

  注意:向下筛选的时候,将目标节点和其子节点比较,谁大就和谁交换位置。

4、插入

  插入节点也很容易,插入时,选择向上筛选,节点初始时插入到数组最后第一个空着的单元,数组容量大小增一。然后进行向上筛选的算法。

  注意:向上筛选和向下不同,向上筛选只用和一个父节点进行比较,比父节点小就停止筛选了。

  

5、完整的Java堆代码

  首先我们要知道用数组表示堆的一些要点。若数组中节点的索引为x,则:

  节点的左子节点是 2*index+1,

  节点的右子节点是 2*index+2,

  节点的父节点是 (index-1)/2。

  注意:"/" 这个符号,应用于整数的算式时,它执行整除,且得到是是向下取整的值。

package com.ys.tree.heap;

public class Heap {

	private Node[] heapArray;
private int maxSize;
private int currentSize; public Heap(int mx) {
maxSize = mx;
currentSize = 0;
heapArray = new Node[maxSize];
} public boolean isEmpty() {
return (currentSize == 0)? true : false;
} public boolean isFull() {
return (currentSize == maxSize)? true : false;
} public boolean insert(int key) {
if(isFull()) {
return false;
}
Node newNode = new Node(key);
heapArray[currentSize] = newNode;
trickleUp(currentSize++);
return true;
}
//向上调整
public void trickleUp(int index) {
int parent = (index - 1) / 2; //父节点的索引
Node bottom = heapArray[index]; //将新加的尾节点存在bottom中
while(index > 0 && heapArray[parent].getKey() < bottom.getKey()) {
heapArray[index] = heapArray[parent];
index = parent;
parent = (parent - 1) / 2;
}
heapArray[index] = bottom;
} public Node remove() {
Node root = heapArray[0];
heapArray[0] = heapArray[--currentSize];
trickleDown(0);
return root;
}
//向下调整
public void trickleDown(int index) {
Node top = heapArray[index];
int largeChildIndex;
while(index < currentSize/2) { //while node has at least one child
int leftChildIndex = 2 * index + 1;
int rightChildIndex = leftChildIndex + 1;
//find larger child
if(rightChildIndex < currentSize && //rightChild exists?
heapArray[leftChildIndex].getKey() < heapArray[rightChildIndex].getKey()) {
largeChildIndex = rightChildIndex;
}
else {
largeChildIndex = leftChildIndex;
}
if(top.getKey() >= heapArray[largeChildIndex].getKey()) {
break;
}
heapArray[index] = heapArray[largeChildIndex];
index = largeChildIndex;
}
heapArray[index] = top;
}
//根据索引改变堆中某个数据
public boolean change(int index, int newValue) {
if(index < 0 || index >= currentSize) {
return false;
}
int oldValue = heapArray[index].getKey();
heapArray[index].setKey(newValue);
if(oldValue < newValue) {
trickleUp(index);
}
else {
trickleDown(index);
}
return true;
} public void displayHeap() {
System.out.println("heapArray(array format): ");
for(int i = 0; i < currentSize; i++) {
if(heapArray[i] != null) {
System.out.print(heapArray[i].getKey() + " ");
}
else {
System.out.print("--");
}
}
}
}
class Node {
private int iData;
public Node(int key) {
iData = key;
} public int getKey() {
return iData;
} public void setKey(int key) {
iData = key;
}
}

  

Java数据结构和算法(十四)——堆的更多相关文章

  1. Java数据结构和算法(四)赫夫曼树

    Java数据结构和算法(四)赫夫曼树 数据结构与算法目录(https://www.cnblogs.com/binarylei/p/10115867.html) 赫夫曼树又称为最优二叉树,赫夫曼树的一个 ...

  2. java数据结构和算法10(堆)

    这篇我们说说堆这种数据结构,其实到这里就暂时把java的数据结构告一段落,感觉说的也差不多了,各种常见的数据结构都说到了,其实还有一种数据结构是“图”,然而暂时对图没啥兴趣,等有兴趣的再说:还有排序算 ...

  3. Java数据结构和算法(四)——栈

    前面我们讲解了数组,数组更多的是用来进行数据的存储,纯粹用来存储数据的数据结构,我们期望的是插入.删除和查找性能都比较好.对于无序数组,插入快,但是删除和查找都很慢,为了解决这些问题,后面我们会讲解比 ...

  4. Java数据结构和算法(四):栈

    一.简介 栈(英语:stack)又称为堆栈或堆叠,栈作为一种数据结构,是一种只能在一端进行插入和删除操作的特殊线性表.它按照先进后出的原则存储数据,先进入的数据被压入栈底(Bottom),最后的数据在 ...

  5. Java数据结构和算法(一)--栈

    栈: 英文名stack,特点是只允许访问最后插入的那个元素,也就是LIFO(后进先出) jdk中的stack源码: public class Stack<E> extends Vector ...

  6. Java数据结构和算法 - 堆

    堆的介绍 Q: 什么是堆? A: 这里的“堆”是指一种特殊的二叉树,不要和Java.C/C++等编程语言里的“堆”混淆,后者指的是程序员用new能得到的计算机内存的可用部分 A: 堆是有如下特点的二叉 ...

  7. Java数据结构和算法(四)--链表

    日常开发中,数组和集合使用的很多,而数组的无序插入和删除效率都是偏低的,这点在学习ArrayList源码的时候就知道了,因为需要把要 插入索引后面的所以元素全部后移一位. 而本文会详细讲解链表,可以解 ...

  8. Java数据结构与算法 - 外部存储

    Q: 什么是外部存储? A: 外部存储特指某类磁盘系统,例如在大多数台式电脑或服务器中的硬盘. Q: 如何访问外部存储? A: 我们所学的数据结构都是假设数据存储在内存中,但是,在很多情况下要处理的数 ...

  9. java数据结构和算法06(红黑树)

    这一篇我们来看看红黑树,首先说一下我啃红黑树的一点想法,刚开始的时候比较蒙,what?这到底是什么鬼啊?还有这种操作?有好久的时间我都缓不过来,直到我玩了两把王者之后回头一看,好像有点儿意思,所以有的 ...

随机推荐

  1. 5.For loops

    for 循环语句   在需要重复执行代码的时候,for循环常常被用到.我们可以让一行代码执行10次:   for i in range(1,11): print(i)   最后一个数字11是不包含在内 ...

  2. main函数是主线程吗

    1.线程的概念: 线程是程序最基本的运行单位,而进程不能运行,所以能运行的,是进程中的线程. 2.线程是如何创建起来的: 进程仅仅是一个容器,包含了线程运行中所需要的数据结构等信息.一个进程创建时,操 ...

  3. spring boot +mysql + mybatis + druid的整理(一)——单数据源

    一,使用spring boot脚手架搭建spring boot框架生成maven项目 如下图所示: 设置自定义的坐标,即左侧的Group和Artifact,右侧可以搜索添加一些依赖,搜索不到的可以在p ...

  4. fb27a9aeaf604597826718c467cc9f4f 为什么我老收到这个

    fb27a9aeaf604597826718c467cc9f4f   为什么我老收到这个fb27a9aeaf604597826718c467cc9f4f   为什么我老收到这个fb27a9aeaf60 ...

  5. 【Java学习笔记之十八】Javadoc注释的用法

    Javadoc注释的用法 Java 文档 // 注释一行/* ...... */ 注释若干行/** ...... */ 注释若干行,并写入 javadoc 文档 通常这种注释的多行写法如下: /*** ...

  6. linux下如何删除文件夹?

    直接rm就可以了,不过要加两个参数-rf 即:rm -rf 目录名字-r 就是向下递归,不管有多少级目录,一并删除:-f 就是直接强行删除,不作任何提示的意思. 例如:删除文件夹实例: rm -rf ...

  7. c语言中标识符的作用域

    1.代码块作用域(block scope) 位于一对花括号之间的所有语句称为一个代码块,在代码块的开始位置声明的标识符具有代码块作用域,表示它们可以被这个代码中的所有语句访问.函数定义的形式参数在函数 ...

  8. Apache/Tomcat/JBOSS/Nginx区别

    先说Apache和Tomcat的区别: Apache是世界使用排名第一的Web服务器软件.它可以运行在几乎所有广泛使用的计算机平台上,由于其跨平台和安全性被广泛使用,是最流行的Web服务器端软件之一. ...

  9. Docker版本升级至17.03

    2017/3/3,Docker官方发表了一篇博客,Docker版本从1.13.*直接跳入17.03,该版本的意思是17年3月.同时,还声明了Docker以后会以CE(Community Edition ...

  10. Use LiveCD to acquire images from a VM

    Forensic examiners usually acquire images from suspect's PC or Laptop. What if the target computer i ...