二叉堆详解及实现优先队列

一 二叉堆概述

二叉堆实际上就是一种特殊的二叉树,完全二叉树,储存为数组结构,如图所示
在这里插入图片描述
数组下标由1开始,是为了更好地描述子父关系

    /**
     * 获取父节点索引
     * @param index
     * @return
     */
    private int parent(int index){
        //等价于 index/2
        return index>>1;
    }

    /**
     * 获取下级左节点索引
     * @param index
     * @return
     */
    private int left(int index){
        //等价于index*2
        return index << 1;
    }

    /**
     * 获取下级右节点索引
     * @param index
     * @return
     */
    private int right(int index){
        //等价于index*2 + 1
        return (index << 1)+1;
    }

二叉堆区分为最大堆、最小堆,最大堆特性每个节点都大于它的两个子节点,最小堆特性每个节点都小于它的两个子节点。根据其特性,最大堆堆顶一定是堆中最大值,最小堆堆顶一定是堆中最小值,本文主要讲解最大堆的实现。

二 优先队列

1.概述

优先队列是一种数据结构,插入与删除元素根据优先级自动排序,下面我们根据最大堆原理实现优先队列,为保证最大堆特性,主要通过上浮与下沉实现

public class MaxPQ<key extends Comparable<key>> {

    /**
     * 存储元素数组
     */
    private key[] pq;
    /**
     * 当前【优先队列】元素个数
     */
    private int n;

    /**
     * 初始化示例
     * @param capacity 数组容量 去除下标为0 实际 capacity+1
     */
    private MaxPQ(int capacity){
        pq = (key[]) new Comparable[capacity+1];
    }

    /**
     * 返回当前队列中最大的元素
     * @return
     */
    private key max(){
        return pq[1];
    }

    /**
     * 队列插入元素
     * 添加元素到最后 调用方法上浮
     * @param e
     */
    private void insert(key e){
        n++;
        pq[n] = e;
        swim(n);
    }

    /**
     * 删除并返回当前队列中最大元素
     */
    private key delMax(){
        // 1.交换堆顶与堆底值
        key max = max();
        swap(1,n);
        // 2.删除堆底值
        pq[n] = null;
        n--;
        // 3.下沉调换后堆顶值
        sink(1);
        return max;
    }

    /**
     * 上浮第k个元素,以维护最大堆性质
     * @param k
     */
    private void swim(int k){
        // k不等于堆最大值且队列第k个元素值大于父节点
        while (k>1 && less(parent(k),k)){
            //交换父节点值 循环上浮
            swap(parent(k),k);
            k = parent(k);
        }
    }

    /**
     * 下沉第k个元素,以维护最大堆性质
     * @param k
     */
    private void sink(int k){
        //如果沉到堆底不再下沉
        while (left(k)<=n){
            // 1.比较子节点左右值大小,获取较大子节点
            int order = left(k);
            if (right(k)<=n && less(order,right(k))){
                order = right(k);
            }
            // 2.当前值比两个子节点都大,无需交换
            if (less(order,k)){
                break;
            }
            // 3.与子节点交换值 循环处理
            swap(order,k);
            k = order;
        }
    }

    /**
     * 交换两个数组
     * @param i
     * @param j
     */
    private void swap(int i, int j){
        key temp = pq[i];
        pq[i] = pq[j];
        pq[j] = temp;
    }

    /**
     * 比较两个队列元素大小 pq[i] 是否小于 pq[j]
     * @param i
     * @param j
     * @return
     */
    private Boolean less(int i,int j){
        return pq[i].compareTo(pq[j])<0;
    }

    /**
     * 获取父节点索引
     * @param index
     * @return
     */
    private int parent(int index){
        //等价于 index/2
        return index>>2;
    }

    /**
     * 获取下级左节点索引
     * @param index
     * @return
     */
    private int left(int index){
        //等价于index*2
        return index << 1;
    }

    /**
     * 获取下级右节点索引
     * @param index
     * @return
     */
    private int right(int index){
        //等价于index*2 + 1
        return (index << 1)+1;
    }
 }

2.插入元素(insert)上浮(swim)调整

在堆底插入元素,假设元素值大于父节点的值,此时最大堆特性被破坏,则需上浮与父节点调换位置,调整后仍然不满足则需循环上浮,如图所示
在这里插入图片描述

3.删除堆顶元素(delMax)下沉(sink)调整

删除堆顶元素,把堆顶元素与堆中最后元素对调,删除,然后通过下沉堆顶元素达到堆平衡,如图所示
在这里插入图片描述

下一篇:二叉堆实现海量数据获取topK问题

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值