【数据结构与算法】第八章:堆(最大堆、最小堆、堆排序、性能测试)

8.1、定义

  • 堆是计算机科学中一类特殊的数据结构的统称,堆通常可以被看做是一棵 完全二叉树数组对象

  • 堆的特性(以大顶堆为例)

    1. 是特殊的 完全二叉树

      除了树的最后一层结点不需要是满的,其它的每一层从左到右都是满的,如果最后一层结点不是满的, 那么要求 左满右不满
      特殊之处:
      结点大于等于它的两个子结点;右子结点也不比它大

    在这里插入图片描述

    1. 通常用 数组 来实现

      具体方法就是将二叉树的结点按照 层级顺序 放入数组中, 根结点在 位置1(数组索引0处不存储数据),它的子结点在位置2和3,而子结点的子结点则分别在位置4,5,6和7,以此类推

      在这里插入图片描述

      • 如果一个结点的位置为 k,则它的父结点的位置为 k/2

      • 两个子结点的位置则分别为 2k2k+1

        这样,在不使用指针的情况下,也可以通过计算数组的索引在树中上下移动:

        从 a[k] 向上一层,就令k等于 k/2

        向下一层就令k等于 2k 或 2k+1

    2. 每个结点都 大于等于 它的两个子结点

      这里要注意堆中仅仅规定了每个结点大于等于它的两个子结点,但这两个子结点的顺序并没有做规定,跟我们之前学习的二叉查找树是有区别的;可以左子结点大于右子结点

      区别于普通的二叉树:
      一般二叉树中右子结点大于父结点

8.2、API

在这里插入图片描述

8.3、实现

1)Insert

堆是用 数组 完成数据元素的存储的,由于数组的底层是一串连续的内存地址,所以要往堆中插入数据,只能往数组中从索引0处开始,依次往后存放数据,但是堆中对元素的顺序是有要求的,每一个结点的数据要 大于等于它的两个子结点的数据,所以每次插入一个元素,都会使得堆中的数据顺序变乱,这个时候就需要通过一些方法,让刚才插入的这个数据放入到合适的位置

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

所以,如果往堆中新插入元素,只需要不断的比较新结点 a[k] 和它的父结点 a[k/2] 的大小,然后根据结果完成数据元素的交换,就可以完成堆的有序调整。

2)delMax

由堆的特性可以知道,索引1处的元素,也就是根结点就 是最大的元素,把根结点的元素删除后,需要有一个新的根结点出现,这时可以 暂时把堆中最后一个元素放到索引1处,充当根结点,但是它有可能不满足堆的有序性需求,这个时候就需要通过一些方法,让这个新的根结点放入到合适的位置
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

所以,当删除掉最大元素后,只需要将最后一个元素放到索引1处,并不断的拿着当前结点 a[k] 与它的子结点a[2k] 和 a[2k+1] 中的较大者交换位置,即可完成堆的有序调整。

3)代码

package chapter06;

/**
 * @author 土味儿
 * Date 2021/9/8
 * @version 1.0
 * 堆
 */
public class Heap<T extends Comparable<T>> {
   
    /**
     * 存储元素的数组
     */
    private T[] items;
    /**
     * 元素个数
     */
    private int n;

    /**
     * 构造器
     *
     * @param capacity
     */
    public Heap(int capacity) {
   
        //this.items = (T[]) new Object[capacity+1];
        this.items = (T[]) new Comparable[capacity+1];
        this.n = 0;
    }

    /**
     * 插入元素t
     *
     * @param t
     */
    public void insert(T t) {
   
        // 在结尾插入
        items[++n] = t;
        // 让新元素上浮,找到合适的位置
        swim(n);
    }

    /**
     * 删除最大元素并返回
     *
     * @return
     */
    public T delMax() {
   
        // 最大元素
        T max = items[1];

        // 交换索引1处 和 最大索引处的元素,让完全二叉树中最右的元素成为临时根结点
        exch(1, n);

        // 删除交换后最大索引处的元素,即原根结点(最大元素)
        items[n] = null;

        // 元素数量减1
        n--;

        // 让新根结点下沉,找到合适位置
        sink(1);

        return max;
    }

    /**
     * 判断索引i处的元素是否比j处的小
     *
     * @param i
     * @param j
     * @return
     */
    private boolean less(int i, int j) {
   
        return items[i].compareTo(items[j]) < 0;
    }

    /**
     * 交换索引i处与j处的元素
     *
     * @param i
     * @param j
     */
    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

土味儿~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值