堆

堆是一种基于树的数据结构,是一种完全二叉树,堆中的所有的节点都按照特定的顺序排列。

在堆数据结构中,如果任意父节点的值都大于其子节点,则会产生一个大顶堆;反之,如果任意父节点的值都小于其子节点,则会产生一个小顶堆 。

上述这种父节点与子节点之间的关系被称为堆属性 。

二叉堆

由于二叉堆是一棵完全二叉树,它可以很容易的被表示为一个数组。在数组中,array[0] 代表根节点,且父节点与子节点的索引关系为:Left Child = 2 * Parent + 1 , Right Child = 2 * Parent + 2。

下图是一个大顶堆的树形结构表示:

其数组表示如下:

[9, 8, 7, 4, 5, 6, 3, 2, 1]

小顶堆

插入元素

该过程包括两个步骤:

  • 将元素插入到堆尾
  • 执行 上浮 操作使其满足堆属性,该过程时间复杂度为 \(O(log N)\)

siftup操作逻辑: 对于小顶堆,若当前节点的元素小于其父节点,则交换这俩个节点的值,然后递归父节点,保证满足堆属性。

@SuppressWarnings("unchecked")
private void siftUp(int k, E x) {
Comparable<? super E> key = (Comparable<? super E>) x;
while (k > 0) {
int parent = (k - 1) >>> 1;
Object e = minHeap[parent]; if (key.compareTo((E) e) >= 0) {
break;
} minHeap[k] = e;
k = parent;
} minHeap[k] = key;
}

删除元素

该过程包括3个步骤:

  • 找到待删除元素的索引位置
  • 将堆中最后一个元素移动到待删除元素的索引位置
  • 对该索引位置元素执行 上浮 或 下沉 操作,该过程时间复杂度为 \(O(log N)\)

siftDown操作逻辑: 对于小顶堆,如果当前节点的值大于其左子节点或右子节点的值,则交换当前节点与子节点中的较小值,然后递归子树保证满足堆属性。

// 将最后一个元素移动到被删除位置
E moved = (E) minHeap[s];
minHeap[s] = null; // 下沉
siftDown(i, moved);
if (minHeap[i] == moved) {
/**
* minHeap[i] == moved
* 表示没有下沉, 说明当前节点与子节点满足堆属性
* 执行上浮, 使得当前节点与父节点满足堆属性
*/
siftUp(i, moved);
if (minHeap[i] != moved) {
return moved;
}
} private void siftDown(int k, E x) {
Comparable<? super E> key = (Comparable<? super E>) x;
int half = heapSize >>> 1; // 非叶子节点
while (k < half) {
int child = (k << 1) + 1;
int right = (k << 1) + 2;
Object c = minHeap[child]; if (right < heapSize && ((Comparable<? super E>) c).compareTo((E) minHeap[right]) > 0) {
c = minHeap[child = right];
} if (key.compareTo((E) c) <= 0) {
break;
} minHeap[k] = c;
k = child;
}
minHeap[k] = key;
}

Java 实现

下述代码为小顶堆的实现,大顶堆的实现也是一样的,区别就是上浮 和 下沉 操作的交换逻辑与小顶堆相反。

package DataStructure;

import java.io.Serializable;
import java.util.NoSuchElementException; public class MinHeap<E> implements Serializable{
private static final int DEFAULT_INITIAL_CAPACITY = 11;
transient Object[] minHeap;
private int heapSize = 0; // 当前堆中的元素个数
private int maxSize; // 堆中最大元素个数 public MinHeap() {
this(DEFAULT_INITIAL_CAPACITY);
} public MinHeap(int initialCapacity) {
if (initialCapacity < 1) {
throw new IllegalArgumentException();
} this.maxSize = initialCapacity;
this.minHeap = new Object[initialCapacity];
} public MinHeap(Collection<? extends E> c) {
initElementsFromCollection(c);
heapify();
} public int size() {
return heapSize;
} public void clear() {
for (int i = 0; i < heapSize; i++) {
minHeap[i] = null;
} heapSize = 0;
} @SuppressWarnings("unchecked")
public E peek() {
return (heapSize == 0) ? null : (E) minHeap[0];
} /**
* 插入元素到堆尾
* @param e
*/
public boolean add(E e) {
if (e == null) {
throw new NullPointerException();
} int i = heapSize;
if (i >= maxSize) {
throw new NoSuchElementException("Heap is full!");
} if (i == 0) {
minHeap[0] = e;
}
else {
siftUp(i, e);
} return true;
} /**
* 删除指定元素
* @param o
* @return
*/
public boolean remove(Object o) {
int i = indexOf(o);
if (i == - 1) {
return false;
}
else {
removeAt(i);
return true;
}
} /**
* 弹出堆顶元素
* @return
*/
@SuppressWarnings("unchecked")
public E poll() {
if (heapSize == 0) {
return null;
} int s = --heapSize;
E result = (E) minHeap[0];
E x = (E) minHeap[s];
minHeap[s] = null;
if (s != 0) {
siftDown(0, x);
} return result;
} private int indexOf(Object o) {
if (o != null) {
for (int i = 0; i < heapSize; i++) {
if (o.equals(minHeap[i])) {
return i;
}
}
} return -1;
} @SuppressWarnings("unchecked")
private E removeAt(int i) {
int s = --heapSize;
if (s == i) {
minHeap[i] = null; // 移除最后一个元素
}
else {
// 将最后一个元素移动到被删除位置
E moved = (E) minHeap[s];
minHeap[s] = null; // 下沉
siftDown(i, moved);
if (minHeap[i] == moved) {
// 没有下沉, 说明当前节点与子节点满足堆属性
// 执行上浮, 使得当前节点与父节点满足堆属性
siftUp(i, moved);
if (minHeap[i] != moved) {
return moved;
}
}
} return null;
} @SuppressWarnings("unchecked")
private void siftUp(int k, E x) {
Comparable<? super E> key = (Comparable<? super E>) x;
while (k > 0) {
int parent = (k - 1) >>> 1;
Object e = minHeap[parent]; if (key.compareTo((E) e) >= 0) {
break;
} minHeap[k] = e;
k = parent;
} minHeap[k] = key;
} @SuppressWarnings("unchecked")
private void siftDown(int k, E x) {
Comparable<? super E> key = (Comparable<? super E>) x;
int half = heapSize >>> 1; // 非叶子节点
while (k < half) {
int child = (k << 1) + 1;
int right = (k << 1) + 2;
Object c = minHeap[child]; if (right < heapSize && ((Comparable<? super E>) c).compareTo((E) minHeap[right]) > 0) {
c = minHeap[child = right];
} if (key.compareTo((E) c) <= 0) {
break;
} minHeap[k] = c;
k = child;
}
minHeap[k] = key;
} private void initElementsFromCollection(Collection<? extends E> c) {
Object[] a = c.toArray();
if (a.getClass() != Object[].class) {
a = Arrays.copyOf(a, a.length, Object[].class);
} int len = a.length;
for (int i = 0; i < len; i++) {
if (a[i] == null) {
throw new NullPointerException();
}
} this.minHeap = a;
this.heapSize = a.length;
} /**
* 根据 minHeap 数组建立小顶堆
*/
@SuppressWarnings("unchecked")
private void heapify() {
// 对所有非叶子节点执行下沉操作
for (int i = (heapSize >>> 1) - 1; i >= 0; i--) {
siftDown(i, (E) minHeap[i]);
}
}
}

优先级队列

PriorityQueue 是堆的一个实现,其默认为小顶堆,可以传入一个比较器使其实现为大顶堆。

public static void main(String[] args) {
// 默认创建小顶堆
PriorityQueue<Integer> minQueue = new PriorityQueue<>(); // 传入比较器, 创建大顶堆
PriorityQueue<Integer> maxQueue = new PriorityQueue<>((o1, o2) -> {return o2 - o1;}); for (int i = 0; i < 10; i++) {
minQueue.add(i);
maxQueue.add(i);
} Iterator<Integer> it1 = minQueue.iterator();
Iterator<Integer> it2 = maxQueue.iterator();
while (it1.hasNext()) {
System.out.print(it1.next() + " ");
}
System.out.println(); while (it2.hasNext()) {
System.out.print(it2.next() + " ");
}
System.out.println();
}

输出结果为:

[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

[9, 8, 5, 6, 7, 1, 4, 0, 3, 2]

堆排序

堆排序算法步骤如下 (升序排列):

  • 对待排序数组建立小顶堆
  • 依次弹出堆顶元素即堆中最小值

该算法时间复杂度为:\(O(NlogN)\)

public static void main(String[] args) {
int[] nums = {2, 3, 1, 6, 4, 7, 5, 8, 9};
List<Integer> list = Arrays.stream(nums).boxed().collect(Collectors.toList());
MinHeap<Integer> minHeap = new MinHeap<>(list); int[] res = new int[nums.length];
int index = 0;
while(minHeap.size() != 0) {
res[index++] = minHeap.poll();
} for (int i = 0; i < res.length; i++) {
System.out.print(res[i] + " ");
}
System.out.println();
}

输出结果为:[1, 2, 3, 4, 5, 6, 7, 8, 9]

Java Heap的更多相关文章

  1. java head space/ java.lang.OutOfMemoryError: Java heap space内存溢出

    上一篇JMX/JConsole调试本地还可以在centos6.5 服务器上进行监控有个问题端口只开放22那么设置的9998端口 你怎么都连不上怎么监控?(如果大神知道还望指点,个人见解) 线上项目出现 ...

  2. Java 堆内存与栈内存异同(Java Heap Memory vs Stack Memory Difference)

    --reference Java Heap Memory vs Stack Memory Difference 在数据结构中,堆和栈可以说是两种最基础的数据结构,而Java中的栈内存空间和堆内存空间有 ...

  3. Tomcat报java.lang.OutOfMemoryError: Java heap space错误停止运行如何解决

    最近开发的一个商业项目,部署完成后,经常出现Tomcat挂掉的现象,报的异常是:java.lang.OutOfMemoryError: Java heap space,上网google了一下,了解了一 ...

  4. MyCAT报java.lang.OutOfMemoryError: Java heap space

    早上同事反映,mycat又假死了,估计还是内存溢出,查看了一下错误日志. INFO | jvm | // :: | java.lang.OutOfMemoryError: Java heap spac ...

  5. Spark java.lang.outofmemoryerror gc overhead limit exceeded 与 spark OOM:java heap space 解决方法

    引用自:http://cache.baiducontent.com/c?m=9f65cb4a8c8507ed4fece7631046893b4c4380146d96864968d4e414c42246 ...

  6. Tomcat 启动项目报错 java.lang.OutOfMemoryError: Java heap space

    近日使用myeclipse 部署web项目,启动tomcat时报错: SEVERE: Error waiting for multi-thread deployment of directories ...

  7. 应用jacob组件造成的内存溢出解决方案(java.lang.OutOfMemoryError: Java heap space)

    http://www.educity.cn/wenda/351088.html 使用jacob组件造成的内存溢出解决方案(java.lang.OutOfMemoryError: Java heap s ...

  8. java.lang.OutOfMemoryError: Java heap space

    java.lang.OutOfMemoryError: Java heap space 原因:内存溢出,内存一直申请一直占用,无法回收 解决方法:定时重启下服务,

  9. java.lang.OutOfMemoryError: Java heap space解决方法

    引起java.lang.OutOfMemoryError: Java heap space异常,可能是由JAVA的堆栈设置太小的原因 根据网上的答案大致有以下两种解决方法: 1.在D:/apache- ...

  10. 【转】java.lang.OutOfMemoryError: Java heap space的解决

    原文地址:http://blog.sina.com.cn/s/blog_4b12778b0100v0bb.html Myeclipse下java.lang.OutOfMemoryError: Java ...

随机推荐

  1. 实现Runnable接口

    1.定义一个类用于实现Runnable接口 2.重写run()方法,编辑代码逻辑体 3.创建线程对象,调用start()开启线程 案例:我在学习 1 public class study implem ...

  2. 学习JavaScript第三周

    字符串的遍历,字符串虽是简单数据类型却有对应的属性和方法,这是因为字符串是包装类型(当然布尔类型和数字类型也是包装类型),临时具有对象的属性和方法,在使用完后就会释放对象. 简单的淡入淡出的轮播图,原 ...

  3. Linux系列---【U盘插入后,linux系统如何查看U盘中的内容?】

    U盘插入后,linux系统如何查看U盘中的内容? 1.插入U盘 2.输入命令查看U盘是否插入成功 sudo fdisk -l 输入上面命令后,在最下面Device Boot一栏查看自己的U盘所在的分区 ...

  4. scroll-view 横向滚动无效

    scroll-view的内层view元素需要: display: inline-block scroll-view的外层元素需要: white-space: nowrap 使得内部组件不换行.

  5. centos7部署teleport堡垒机

    Centos7.9部署Teleport堡垒机 简介 Teleport是一款简单易用的开源堡垒机系统,具有小巧.易用的特点,支持 RDP/SSH/SFTP/Telnet 协议的远程连接和审计管理.Tel ...

  6. 【原创】android 7.0 通知报错 java.lang.SecurityException: You need MANAGE_USERS permission to: check if specified user a managed profile outside your profile group

    项目中在后台发送通知,突然某一天测出在Android 7.0上通知发送失败,那么根据提示,我们尝试加了MANAGE_USERS权限,看起来是个系统级别权限,验证后果然无效.接着在搜索后都无果,似乎大家 ...

  7. 9.6 2020 实验 1:Mininet 源码安装和可视化拓扑工具

    一.实验目的 掌握 Mininet 的源码安装方法和 miniedit 可视化拓扑生成工具.   二.实验任务 使用源码安装 Mininet 的 2.3.0d6 版本,并使用可视化拓扑工具生成一个最简 ...

  8. cmake使用boost静态库,错误提示 Could NOT find Boost (missing: Boost_INCLUDE_DIR) (Required is at least version "1.48")

    使用的是Cmake-gui 编译. 问题出在C盘路径下找不到 Boost ,是否需要把boost的路径添加到系统Path 中? 任然不能解决. 更改源码: 找到下面这几行代码(你可以搜索) messa ...

  9. mysqli语句的用法

    改用了PHP7的环境后 mysql语句好像就不能用了.. 有点懵逼 就写一下该怎么用以免遗忘 ======= 1 链接数据库 $link=mysqli_connect('127.0.0.1:xxxx' ...

  10. ABP vNext微服务架构详细教程——简介

    概述 该系列文章主要展示ABP vNext框架在微服务架构下的用法,提供一套可落地的技术实现思路,并演示各服务在Kubernetes下的部署方案. 基础概念 ABP vNext:基于ASP.NET C ...