Bootstrap

最大堆(二叉堆)

1、堆也是一种数据结构,使用二叉堆去实现
2、二叉堆是完全二叉树,类似于下图,必须要满足从根到每一层都顺序堆放,如果某一层放满了,则继续从下一层从左往右的顺序继续执行。
在这里插入图片描述
3、最大堆的定义:堆中某个节点的值总是不大于父亲节点的值。
4、实现最大的堆的结构,其实在内部我们可以采用数组的形式去实现,因为根据索引,每个节点(如果索引是i)的父亲节点是 (i-1)/2, 则左孩子的节点索引是i2+1, 右孩子节点的索引是 i2+2 ,这样我们就可以任意的根据某个索引的位置,寻找其父亲的节点,以及左右子树的节点。

实现最大堆的类的内部方法介绍

  1. 首先内部定义一个私有的成员变量,使用ArrayList
private Array<E> data;
  1. public int size() // 返回堆中的元素个数,我们可以直接返回数组的大小
  2. public boolean isEmpty() // 返回堆是否为空,我们可以返回数组是够为空
  3. private int parent(int index) // 返回父亲节点的索引
  4. private int leftChild(int index) // 返回左子树根节点的索引
  5. private int rightChild(int index) // 返回右子树根节点的索引
  6. public void add(E e) // 向堆中添加元素。其实也就是如果在数组中合理的添加元素呢?其实我们可以首先在数组的末尾添加元素,然后再根据该节点和父亲节点的值进行比较,如果大于父亲节点的值,则交换位置,然后把当前索引修改为父亲节点的索引,以此类推,不断上浮的判断,判断结束的终止条件则是判断的节点是否还有父亲节点,或者在中途判断的时候,该节点的值已经小于了父亲节点,则上浮的过程终止,最终完成了添加元素的操作。
  7. public E findMax() // 查看最大元素,直接返回索引为0的值
  8. public E extractmax() // 取出最大值,我们可以把索引为0和索引为最后一个位置的元素进行位置的交换,然后删除交换位置后的最后索引位置的元素,然后对索引为0 的位置的元素进行下沉操作,和上浮操作的思路一致。返回返回之前我们保存的最大值。其中下浮操作我们可以这样来考虑:首先判断该节点是否右左孩子的节点,如果有则保存该左孩子节点的索引,然后判断是否有右孩子,如果没有,则判断左孩子的值和父亲节点值的大小,如果大于父亲节点的值,则交换元素,否则不交换。如果有右孩子,则先判断左右孩子的值得大小,去较大孩子的索引,然后判断与父亲节点值得大小,如果大于父亲节点的值,则交换元素位置,否则不交换,终止下沉操作。
  9. 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;
    }

}

是不是有帮助到小伙伴呢?同时也是帮助我巩固了具体实现方法的思路。继续努力,大家加油!

;