java实现最大堆
优先队列
普通的队列是一种先进先出的数据结构,元素在队列尾追加,而从队列头删除。在优先队列中,元素被赋予优先级。当访问元素时,具有最高优先级的元素最先删除。优先队列具有最高级先出 (first in, largest out)的行为特征。通常采用堆数据结构来实现。
堆
堆(英语:heap)是计算机科学中一类特殊的数据结构的统称。堆通常是一个可以被看做一棵树的数组对象。堆总是满足下列性质:
- 堆中某个节点的值总是不大于或不小于其父节点的值;
- 堆总是一棵完全二叉树。
堆的实现通过构造二叉堆(binary heap),实为二叉树的一种;由于其应用的普遍性,当不加限定时,均指该数据结构的这种实现。这种数据结构具有以下性质。
- 任意节点小于(或大于)它的所有后裔,最小元(或最大元)在堆的根上(堆序性)。
- 堆总是一棵完全树。即除了最底层,其他层的节点都被元素填满,且最底层尽可能地从左到右填入。
将根节点最大的堆叫做最大堆或大根堆,根节点最小的堆叫做最小堆或小根堆。
我们这里讲的是二叉堆。
堆的入队和出队的时间复杂度都是O(log n)
上图就是一个最大堆的事例
下面我们使用数组来构建一个最大堆,在这里为了便于理解,数组索引为0的节点不存放数值,从第二个节点开始存放数据。
当前节点的父节点、左孩子、右孩子的索引就会有如下的关系:
- 父节点的索引:index/2 (index为当前节点的索引)
- 左孩子的索引:index*2
- 右孩子的索引:index*2+1
如果从数组的第一个节点开始存放数据的话,当前节点的父节点、左孩子、右孩子的索引就会有如下的关系:
父节点的索引:(index-1)/2 (index为当前节点的索引)
左孩子的索引:index*2+1
右孩子的索引:index*2+2
最大堆的基础架构
import java.util.ArrayList;
public class MaxHeap<E extends Comparable<E>> {
//这里使用数组来实现
private ArrayList<E> data;
public MaxHeap(int capacity) {
data = new ArrayList<>(capacity);
}
public MaxHeap() {
data = new ArrayList<>();
}
//返回堆中元素的个数
public int getSize() {
return data.size();
}
//判断堆是否为空
public boolean isEmpty() {
return data.isEmpty();
}
//返回完全二叉树中索引为index的节点的父节点的索引
private int parent(int index) {
if (index == 0)
throw new IllegalArgumentException("index-0 doesn't have parent");
return (index - 1) / 2;
}
//返回完全二叉树中索引为index的节点的左孩子的索引
private int leftChild(int index) {
return index * 2 + 1;
}
//返回完全二叉树中索引为index的节点的右孩子的索引
private int rightChild(int index) {
return index * 2 + 2;
}
//交换索引为i、j的值
private void swap(int i, int j) {
E t = data.get(i);
data.set(i, data.get(j));
data.set(j, t);
}
}
往堆中添加元素
- 再向堆中添加元素时,除了要维持完全二叉树的结构,还要注意堆的约束条件:根节点的值要大于左右子树的值。
在这里因为我们使用数组来实现的堆,所以添加元素时,我们可以先将元素添加到数组的末尾,然后循环的与父节点比较大小,比父节点大就与父节点交换位置,之后就继续与新的父节点比较大小,直到小于等于父节点。
- 如上图所示,我们要在这个堆中添加一个元素36
- 先将元素添加到数组的末尾。
- 然后通过当前的索引计算出父节点的索引,通过索引得到父节点的值16,通过比较新添加的节点比其父节点大,所以将新添加的值与父节点交换在数组中的位置。之后再与新的父节点41比较,36<41,结束操作。
添加元素的代码实现
//向堆中添加元素
public void add(E e) {
data.add(e);
siftUp(data.size() - 1);
}
private void siftUp(int k) {
//k不能是根节点,并且k的值要比父节点的值大
while (k > 0 && data.get(parent(k)).compareTo(data.get(k)) < 0) {
swap(k, parent(k));
k = parent(k);
}
}
删除堆顶元素
删除堆顶元素要注意维持堆的特殊性质。这里举个例子。
- 要将这个堆中删除最大值,也就是堆顶元素62,先将62取出。
- 将堆顶元素和堆的最后一个元素互换,也就是数组的首尾元素互换。
- 删除最后一个元素,也就是堆中的最大值
- 将当前的堆顶元素16的左右孩子41、30进行比较,找出最大的一个41,再与根节点16进行比较,左孩子41比根节点16大,所以将根节点与其左孩子互换,如上图所示。
- 重复上面的操作,直到当前节点的值大于其左右子树。过程如下所示。
删除堆顶元素的代码实现
//看堆中最大的元素
public E findMax() {
if (data.size() == 0)
throw new IllegalArgumentException("Can not findMax when heap is empty");
return data.get(0);
}
//取出堆中最大的元素
public E extractMax() {
E ret = findMax();
swap(0, data.size() - 1);
data.remove(data.size() - 1);
siftDown(0);
return ret;
}
private void siftDown(int k) {
//leftChild存在
while (leftChild(k) < data.size()) {
int j = leftChild(k);
//rightChild存在,且值大于leftChild的值
if (j + 1 < data.size() &&
data.get(j).compareTo(data.get(j + 1)) < 0)
j = rightChild(k);
//data[j]是leftChild和rightChild中最大的
if (data.get(k).compareTo(data.get(j)) >= 0)
break;
swap(k, j);
k = j;
}
}
Replace操作
Replace是指将堆中的最大元素取出,替换另一个进去。
自然地我们会想到使用之前的extractMax()和add()来实现,但是这样的时间复杂度将会是两次的O(log n),因此我们可以直接将堆顶元素替换以后执行sift down操作,这样时间复杂度就只有O(log n)。
Replace代码实现
//取出堆中最大的元素,替换为元素e
public E replace(E e){
E ret = findMax();
data.set(0, e);
siftDown(0);
return ret;
}
Heapify操作
Heapify是指将数组转化为堆
这里我们先将数组直接看成是一个完全二叉树,然后找到这棵二叉树的最后一个非叶子节点的节点,也就是该树的最后一个节点的父节点。然后从这个节点开始到根节点结束,执行sift down操作。
这样的时间复杂度为O(n)
Heapify代码实现
//heapify操作:将数组转化为堆
public MaxHeap(E[] arrs) {
data = new ArrayList<>(Arrays.asList(arrs));
for (int i = parent(data.size() - 1); i >= 0; i--) {
siftDown(i);
}
}
java实现最大堆的更多相关文章
- java 创建最大堆
最大堆的性质是除了根节点之外的所有节点(i)都需要满足A[PARENT(i)]>A[i],即其对应节点值小于其父节点对应值. 下面实现以数组int []a构建最大堆. public class ...
- java基础之:堆排序
最近做题目饱受打击,愈发觉得打好基础的重要性,于是乎,决心把基本的排序算法还有数组操作一一实现,目的在于一方面能够得到对JAVA基础的巩固,另一面在实现的过程中发现不足. 今天所实现的堆排序(最大堆) ...
- 二叉堆(三)之 Java的实现
概要 前面分别通过C和C++实现了二叉堆,本章给出二叉堆的Java版本.还是那句话,它们的原理一样,择其一了解即可. 目录1. 二叉堆的介绍2. 二叉堆的图文解析3. 二叉堆的Java实现(完整源码) ...
- OC、C#与JAVA语法特点一些异同(差集&交集)
C#对JAVA: 1.扩展方法 2.部分类 3.动态对象 4.匿名返回类型 5.表达式树 6.Linq 7.没有函数指针,委托,事件的直接提供方式 8.JAVA接口不规定以I开头,这个很烂的思想! J ...
- Java进阶 JVM 内存与垃圾回收篇(一)
JVM 1. 引言 1.1 什么是JVM? 定义 Java Vritual Machine - java 程序的运行环境(Java二进制字节码的运行环境) 好处 一次编译 ,到处运行 自动内存管理,垃 ...
- .NET技术在中国为什么老被人嫌弃
这个话题有点自黑的意思,我从.NET 1.1开始玩.NET,到现在已经11年了,我是看着.NET成长起来,在中国壮大的,也见证了近几年.NET被各种嫌弃,其实说到底还是中国的架构师太少,我是说真正懂行 ...
- 转载:ZooKeeper Programmer's Guide(中文翻译)
本文是为想要创建使用ZooKeeper协调服务优势的分布式应用的开发者准备的.本文包含理论信息和实践信息. 本指南的前四节对各种ZooKeeper概念进行较高层次的讨论.这些概念对于理解ZooKeep ...
- ZooKeeper程序员指南(转)
译自http://zookeeper.apache.org/doc/trunk/zookeeperProgrammers.html 1 简介 本文是为想要创建使用ZooKeeper协调服务优势的分布式 ...
- zookeeper[1] (转)ZooKeeper Programmer's Guide(zookeeper编程向导)---中文
原文:http://www.cnblogs.com/Xrinehart/p/3512509.html 本文是为想要创建使用ZooKeeper协调服务优势的分布式应用的开发者准备的.本文包含理论信息和实 ...
随机推荐
- Android开发之动态创建多个按钮
//获取屏幕大小,以合理设定 按钮 大小及位置 DisplayMetrics dm = new DisplayMetrics(); getWindowManager().getDefaultDispl ...
- 解决SurfaceView调用setZOrderOnTop(true)遮挡其他控件的问题
SurfaceView遮挡其他控件的项目背景: 最近在做播放器项目,由于底层实现是用Surface和OpenGL切换渲染,所以在布局里面同时使用了GLSurfaceView和SurfaceView,同 ...
- SpringBoot学习8:springboot整合freemarker
1.创建maven项目,添加pom依赖 <!--springboot项目依赖的父项目--> <parent> <groupId>org.springframewor ...
- Junit 测试 @Test 红名问题
准备测试,如上图,都是红名 Ctrl+1 或者鼠标放在@Test上 鼠标放在@Test上出现上面这种就可直接点击安装了 如果是下边这种 Test is not an annotation type,一 ...
- 总结JavaScript常用数组操作方法,包含ES6方法
一.concat() concat() 方法用于连接两个或多个数组.该方法不会改变现有的数组,仅会返回被连接数组的一个副本. var arr1 = [1,2,3]; var arr2 = [4,5]; ...
- CSU 1216异或最大值 (0-1 trie树)
Description 给定一些数,求这些数中两个数的异或值最大的那个值 Input 多组数据.第一行为数字个数n,1 <= n <= 10 ^ 5.接下来n行每行一个32位有符号非负整数 ...
- Mysql5.7创建存储过程中调用自定义函数报错Not allowed to return a result set from a function
因为很多存储过程都会共用一段sql语句,所以我把共用的sql封装成一个自定义函数 AddCapital(); 然后通过存储过程调用,创建存储过程会报错1415,Not allowed to retur ...
- Linux - bashrc之alias
1. cd ~ 2. touch .bashrc // 若该文件不存在的话 3. vim .bashrc ----------------复制粘贴如下文本--------------- # alias ...
- (转)IDE 而言,是 Xcode 的技术比较先进还是 Visual Studio?
李如一他们弄得那个IT公论,最近有一期是吐槽ObjC的.吐到最后, @涛 吴 说,理想的用户界面语言应该是界面的描述和逻辑分开的,想了半天举不出例子来,其实说的不就是WPF吗?还在用Interface ...
- js如何获得系统时间年月日时分秒
javascript 自带有个对象(构造函数),Date().下面是代码: 回答一: var now = new Date(); var nowTime = now.toLocaleString() ...