用堆模拟实现优先级队列

一、堆的性质:

  • 堆中某个节点的值总是不大于或不小于其父节点的值
  • 堆总是一棵完全二叉树

二、堆向下调整

    // array 表示用来存储堆的数组.
    // size 表示数组上有效元素的个数(有效元素不一定和 array.length 一样)
    // index 表示从哪个位置开始进行向下调整
    // 以大堆为例
    public static void shiftDown(int[] array, int size, int index) {
        int parent = index;
        int child = 2 * parent + 1;//左子树下标
        // 循环条件的含义:
        // 如果 child < size, 就意味着 child 是存在的.
        // 如果 child >= size, 就说明 parent 已经是叶子节点.
        while (child < size) {
            // 第一次比较, 找出左右子树的最大值
            if (child + 1 < size && array[child + 1] > array[child]) {
                // 如果发现右子树存在, 且右子树比左子树大,
                // 就让 child 指向右子树
                child = child + 1;
            }
            // 这个条件结束后, child 指向左右子树中较大的那个
            // 第二次比较, 拿刚才的 child 位置的元素和 parent 进行比较
            // 看是否符合大堆的要求. 如果不符合就交换两个元素
            if (array[parent] < array[child]) {
                // 不符合大堆要求,交换
                int tmp = array[parent];
                array[parent] = array[child];
                array[child] = tmp;
            } else {
                // 调整就完成了, 刚才 index 位置的节点已经被放到合适的位置了
                // 不需要继续调整了
                break;
            }
            // 更新循环变量
            parent = child;
            child = 2 * parent + 1;
        }
    }

三、堆向上调整 

    //向上调整,以大堆为例
    public static void shiftUp(int[] array, int size, int index) {
        int child = index;
        int parent = (child - 1) / 2;
        //如果 child 为 0,说明已经调整到最上面了
        while (child > 0) {
            // 比较父节点和子节点是否符合堆的要求
            if (array[parent] < array[child]) {
                // 如果不符合大堆的要求, 就交换这两个元素
                int tmp = array[parent];
                array[parent] = array[child];
                array[child] = tmp;
            } else {
                // 只要发现现在的父子节点符合堆要求
                // 就认为现在这个堆已经调整完毕了
                break;
            }
            // 更新循环变量
            child = parent;
            parent = (child - 1) / 2;
        }
    }

四、堆的创建

    // createHeap 方法, 看起来时间复杂度是 O(NlogN)
    // 实际上时间复杂度是 O(N)
    // 基于向下调整的建堆操作
    public static void createHeap(int[] array) {
        // 从后往前遍历数组,针对每个下标都进行向下调整
        // 最后一个元素的下标是 length - 1
        // 向下调整建堆是从最后一个非叶子节点开始往前循环
        // 最后一个非叶子节点正是最后一个节点的父节点
        for (int i = (array.length - 1 - 1) / 2; i >= 0; i--) {
            shiftDown(array, array.length, i);
        }
    }

五、堆的插入和删除

    // 为了让后续的堆操作比较方便, 使用成员变量的方式来保存堆对应的数组
    private int[] data = new int[1000];
    private int size = 0;
    public MyPriorityQueue(int[] data, int size) {
        this.size = size;
        for (int i = 0; i < size; i++) {
            this.data[i] = data[i];
        }
    }

    //往堆中插入元素
    public void offer(int val) {
        if (size >= data.length) {
            // 堆已经满了,插入失败
            return;
        }
        // 1. 先把新元素放到数组的末尾(尾插)
        data[size] = val;
        size++;
        // 2. 加入新的元素之后,可能就破坏了原有堆的结构
        //    就需要调整堆,让堆能够重新符合要求
        //    通过向上调整的方式,从新元素的位置进行向上调整
        //从最后一个元素开始进行调整
        shiftUp(data, size, size - 1);
    }
    
    // 删除堆顶元素
    public Integer poll() {
        if (size == 0) {
            return null;
        }
        Integer result = data[0];
        // 先把 0 号元素和最后一个元素互换
        // 或者直接把最后一个元素给赋值到 0 号元素
        data[0] = data[size - 1];
        // 删除最后一个元素
        size--;
        // 从 0 号位置开始进行向下调整
        shiftDown(data, size, 0);
        return result;
    }

 

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值