java删除最大堆根节点,最大堆的简单实现

本文详细介绍了最大堆的概念,包括完全二叉树的定义,最大堆的性质以及如何通过数组实现最大堆。讨论了添加元素、查看最大元素、取出最大元素的操作,并展示了节点上浮和下沉的过程,同时提供了构造最大堆的代码示例。
摘要由CSDN通过智能技术生成

二叉堆是一棵完全二叉树,完全二叉树:对于深度为K的,有n个结点的二叉树,当且仅当其每一个结点都与深度为K的满二叉树中编号从1至n的结点一一对应时称之为完全二叉树。

可以理解为将数据按照“层序遍历”的方式排列到二叉树的每一个节点,完全二叉树不一定是满二叉树,完全二叉树缺少的部分在树的右下方。

20200321100211548941.png

如果堆中某个节点的值总是不大于其父节点的值,那么这种堆就是最大堆。

由于最大堆的元素是按照一层一层的顺序排列的,所以我们可以使用数组来作为底层数据结构来实现最大堆,当然也可以使用树结构实现。

20200321100211910283.png

以数组的形式表现的最大堆差不多是这样的:

20200321100211968879.png

那么要怎样区分哪些节点是父节点,哪些节点是孩子节点?

我们可以发现左孩子节点的偏移是父节点的2倍然后+1,右孩子节点是父节点的2倍+2。

parent(i) = (i - 1) / 2

leftChild(i) = 2 * i + 1

rightChild(i) = 2 * i +2

用我之前的动态数组来实现最大堆:

public class MaxHeap> {

//使用动态数组作为基础数据结构实现最大堆

private Array data;

//构造函数---指定容量

public MaxHeap(int capacity) {

data = new Array(capacity);

}

public MaxHeap() {

data = new Array();

}

}

接下来是获取各个节点索引的方法的实现:

//返回完全二叉树的数组表示中,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;

}

两个简单的方法:

//返回堆中的元素个数

public int size() {

return data.getSize();

}

//判断堆中是否为空

public boolean isEmpty() {

return data.isEmpty();

}

向最大堆中添加元素:

如果添加的元素比父节点还大,那么就破坏了最大堆的定义,因此我们需要调整,把添加的元素和父节点对比,然后判断是否更换位置,如果更换位置了,那么就要再对比新的父节点,然后判断是否更换位置,以此类推。

比如我添加了一个元素35:

20200321100212003060.png

可以看到35比父节点还大,所以要更换位置("上浮"),效果差不多是这样的:

20200321100212042124.png

但是我们更换位置后,却发现比新的父节点还大,因此还要更换位置:

20200321100212090954.png

元素这样排列才是符合最大堆定义的。

具体的"上浮"方法和添加元素方法:

//上浮方法

private void siftUp(int i) {

//i不能到达根节点,因为根节点没有父节点

while (i > 0 && data.get(parent(i)).compareTo(data.get(i)) < 0) {

data.swap(i, parent(i));

//重新赋值i

i = parent(i);

}

}

//向堆中添加元素

public void add(E e) {

data.addLast(e);

//上浮,传入将可能要上浮的元素所在的索引

siftUp(data.getSize() - 1);

}

查看堆中最大元素的方法

//查看堆中的最大元素

public E findMax() {

if (data.getSize() == 0)

throw new IllegalArgumentException("Heap is Empty");

return data.get(0);

}

从堆中取出最大元素:

在最大堆中只能取出堆中最大的元素,我们把根节点取出删除后,堆就变成两棵树,把两棵树合成一棵比较麻烦,我们可以先将最后一个节点换到根节点,然后把最后一个节点删除,但是此时的堆是不满足最大堆的定义的,因此需要对根节点进行"下沉"操作。

交换元素

20200321100212164199.png

20200321100212209123.png

在删除最后一个节点后,就需要对根节点进行"下沉"操作了,首先找出根节点的左右两个孩子节点中最大的一个,将其与根节点进行比较,如果大于根节点,就需要进行交换节点操作,以此循环。

20200321100212266742.png

20200321100212311666.png

继续"下沉":

20200321100212348777.png

20200321100212384911.png

可以看到7比11还小,还需要下沉:

20200321100212422022.png

20200321100212461086.png

这样才符合最大堆的定义。

下沉方法和取出最大元素的方法:

//下沉方法

private void siftDown(int i) {

//i的左孩子节点都已经越界了,就说明i已经没有孩子了

while (leftChild(i) < data.getSize()) {

//用变量j存储左孩子的索引

int j = leftChild(i);

//如果j+1 < getSize,就说明有右孩子节点,并且右孩子节点大于左孩子节点

if (j + 1 < data.getSize() && data.get(j + 1).compareTo(data.get(j)) > 0)

//将j赋值为右孩子节点的索引

j = rightChild(i);

//如果i索引节点大于或等于j索引的节点,那么不会破坏最大堆的定义,break

if (data.get(i).compareTo(data.get(j)) >= 0)

break;

//否则交换i和j

data.swap(i, j);

//重新赋值i

i = j;

}

}

//取出堆中的最大元素

public E extractMax() {

E ret = findMax();

data.swap(0, data.getSize() - 1);

data.removeLast();

siftDown(0);

return ret;

}

替换堆中最大的元素方法:

//取出堆中的最大元素,并且替换成元素e

public E replace(E e){

E ret = findMax();

data.set(0,e);

siftDown(0);

return ret;

}

最后可以写个构造函数用于传入一个数组然后构造出最大堆:

//构造函数---传入arr数组并且构造最大堆

public MaxHeap(E[] arr) {

data = new Array<>(arr);

for (int i = parent(arr.length - 1); i >= 0; i--) {

siftDown(i);

}

}

20200321100212499173.png

这里for循环的变量i控制的是每个非叶子节点的顺序,从最后一个非叶子节点到根节点的顺序。 siftDown保证了每个非叶子节点的"下沉"。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值