1、堆也是一种数据结构,使用二叉堆去实现
2、二叉堆是完全二叉树,类似于下图,必须要满足从根到每一层都顺序堆放,如果某一层放满了,则继续从下一层从左往右的顺序继续执行。
3、最大堆的定义:堆中某个节点的值总是不大于父亲节点的值。
4、实现最大的堆的结构,其实在内部我们可以采用数组的形式去实现,因为根据索引,每个节点(如果索引是i)的父亲节点是 (i-1)/2, 则左孩子的节点索引是i2+1, 右孩子节点的索引是 i2+2 ,这样我们就可以任意的根据某个索引的位置,寻找其父亲的节点,以及左右子树的节点。
实现最大堆的类的内部方法介绍
- 首先内部定义一个私有的成员变量,使用ArrayList
private Array<E> data;
- public int size() // 返回堆中的元素个数,我们可以直接返回数组的大小
- public boolean isEmpty() // 返回堆是否为空,我们可以返回数组是够为空
- private int parent(int index) // 返回父亲节点的索引
- private int leftChild(int index) // 返回左子树根节点的索引
- private int rightChild(int index) // 返回右子树根节点的索引
- public void add(E e) // 向堆中添加元素。其实也就是如果在数组中合理的添加元素呢?其实我们可以首先在数组的末尾添加元素,然后再根据该节点和父亲节点的值进行比较,如果大于父亲节点的值,则交换位置,然后把当前索引修改为父亲节点的索引,以此类推,不断上浮的判断,判断结束的终止条件则是判断的节点是否还有父亲节点,或者在中途判断的时候,该节点的值已经小于了父亲节点,则上浮的过程终止,最终完成了添加元素的操作。
- public E findMax() // 查看最大元素,直接返回索引为0的值
- public E extractmax() // 取出最大值,我们可以把索引为0和索引为最后一个位置的元素进行位置的交换,然后删除交换位置后的最后索引位置的元素,然后对索引为0 的位置的元素进行下沉操作,和上浮操作的思路一致。返回返回之前我们保存的最大值。其中下浮操作我们可以这样来考虑:首先判断该节点是否右左孩子的节点,如果有则保存该左孩子节点的索引,然后判断是否有右孩子,如果没有,则判断左孩子的值和父亲节点值的大小,如果大于父亲节点的值,则交换元素,否则不交换。如果有右孩子,则先判断左右孩子的值得大小,去较大孩子的索引,然后判断与父亲节点值得大小,如果大于父亲节点的值,则交换元素位置,否则不交换,终止下沉操作。
- public E replace(E e) // 用元素e 替换最大元素,我们可以在索引为0的位置利用数组的方法进行set(0,e),然后再对索引为0 的元素进行下沉操作。
具体的代码实现
/**
* Created with IntelliJ IDEA.
*
* @Author: xiaocl
* @Date: 2021/06/11/14:24
* @Description:最大堆
*/
public class MaxHeap<E extends Comparable<E>> {
private Array<E> data;
// 构造函数
public MaxHeap(int capacity) {
data = new Array<>(capacity);
}
public MaxHeap() {
data = new Array<>();
}
public MaxHeap(E[] arr){
data = new Array<>(arr);
for (int i = parent(arr.length-1); i >=0 ; i--) {
siftDown(i);
}
}
// 返回堆中的元素
public int size() {
return data.getSize();
}
// 返回一个布尔值,判断堆是否为空
public boolean isEmpty() {
return data.isEmpty();
}
// 返回父亲索引
private int parent(int index) {
if (index == 0) {
throw new IllegalArgumentException("index-0 doesn't have parent");
}
return (index - 1) / 2;
}
// 返回左孩子的索引
private int leftChild(int index) {
return index * 2 + 1;
}
// 返回右孩子的索引
private int rightChild(int index) {
return index * 2 + 2;
}
// 向堆中添加元素
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(parent(k), k);
k = parent(k);
}
}
// 看堆中最大的元素
public E findMax() {
if (data.getSize() == 0) {
throw new IllegalArgumentException("Cannot find max when heap is empty");
}
return data.get(0);
}
// 从堆中取出最大元素
public E extractmax() {
E ret = findMax();
data.swap(0, data.getSize() - 1);
data.removeLast();
siftDown(0);
return ret;
}
// 下沉操作
private void siftDown(int k) {
// 判断 K 的索引是否还有孩子
while (leftChild(k) < data.getSize()) {
// 左孩子的索引
int j = leftChild(k);
// 有右孩子
// 并判断两者的大小,取较大的索引
if (j + 1 < data.getSize() && data.get(j + 1).compareTo(data.get(j)) > 0) {
j = rightChild(k);
}
// 判断与最大孩子的大小
if (data.get(k).compareTo(data.get(j)) >= 0) {
break;
}
data.swap(k, j);
k = j;
}
}
// 取出最大元素,替换操作,并替换e这个元素
public E replace(E e) {
E ret = findMax();
data.set(0,e);
siftDown(0);
return ret;
}
}
是不是有帮助到小伙伴呢?同时也是帮助我巩固了具体实现方法的思路。继续努力,大家加油!