为了账号安全,请及时绑定邮箱和手机立即绑定

数据结构——堆

标签:
Java 算法

堆是一种完全二叉树(每个结点最多有两个子节点,且结点总是有限从最左边排列)。下图是一个最大堆(父节点总是比子节点大)。

需要注意的是左右两个子节点之间并没有确定的大小关系。
201111231437353800.png
最大堆

由于堆的定义,堆具有如下性质:

  • 位置为k的节点孩子节点位置为2k, 2k+1;
  • 位置为k的节点的父节点位置为[2/k]向下取整。

所以堆可以使用数组来存储。为了计算方便,一般把数组的第一个位置下标0空出来,从下标1开始放堆的元素

堆的接口

以下是常见的堆接口, 代码地址

/**
 * 堆接口
 */
public interface IHeap<T> {
    int size(); // 当前元素的长度
    boolean isEmpty(); // 判空
    void add(T data); // 添加
    T pop(); // 弹出堆顶元素
    T remove(int i); // 移除某位置元素
    T peek(); // 过去堆顶元素
}
堆的普通实现

代码地址

成员变量

为了灵活控制对的排序方式,这里使用了泛型和Comparator接口。

public class MyHeap<T> implements IHeap<T> {
    T[] datas; // 存放数据的数组 datas[1..n]
    private int count = 0; // 计数
    private Comparator<T> comparator; // 比较器
    // ...
}
私有的辅助方法

为了代码易读性,增加了两个私有方法:

    /**
     * 比较两个数,看谁上浮
     * @param i 第一个数
     * @param j 第二个数
     * @return true 为i上浮, false 为j上浮
     */
    private boolean needSwim(int i, int j) {
        return comparator.compare(datas[i], datas[j]) < 0;
    }

    /**
     * 交换两个数
     * @param i 第一个数的位置
     * @param j 第二个数的位置
     */
    private void exchange(int i, int j) {
        T temp = datas[i];
        datas[i] = datas[j];
        datas[j] = temp;
    }
上浮与下沉

堆的主要操作之一,通过与父节点或子节点的不断比较与交换,最终维护堆的性质。其中下沉要稍微复杂一点,因为要判断与左孩子交换还是右孩子交换,同时需要做边界判定。

    /**
     * 上浮
     * @param k 开始上浮的位置
     */
    private void swim(int k) {
        while (k > 1 && needSwim(k, k / 2)) {
            exchange(k, k / 2);
            k /= 2;
        }
    }

    /**
     * 下沉
     * @param k 开始下沉的位置
     */
    private void sink(int k) {
        while (2 * k <= count) {
            int j = 2 * k;
            // 判断是否超出范围,再判断j 和 j + 1 那个更需要上浮
            if (j < count && needSwim(j + 1, j))
                j++; // 跟需要上浮的那个换
            if (needSwim(j, k))
                exchange(k, j);
            else break;
            k = j;
        }
    }
构造函数

构造函数主要有两个,分别是:

  • 建空堆
  • 使用数组建堆

    // 使用空数组构造,容量为capacity
    public MyHeap(int capacity, Comparator<T> comparator) {
        this.datas = (T[]) new Object[capacity + 1];
        this.comparator = comparator;
    }
    
    // 使用已给的数组构造,容量为数组长度
    public MyHeap(T[] datas, Comparator<T> comparator) {
        this.count = datas.length;
        this.datas = (T[]) new Object[count + 1];
        this.comparator = comparator;
        System.arraycopy(datas, 0, this.datas, 1, count);
        // 对前半部分逆序下沉,构造堆
        for (int k = count / 2; k >= 1; k--)
            sink(k);
    }
测试

测试代码

点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消