MyPriorityQueue

PriorityQueue 优先队列,它的数据结构是数组实现的队列,但体现形式是一棵二叉堆树结构。在元素存放时,通过对存放元素的比较和替换形成二叉堆结构。

二叉堆是一种特殊结构的堆,它的表现形态可以是一棵完整或近似二叉树的结构。例如,我们在本章节中实现的延迟队列中的元素存放,使用的就是 `PriorityQueue` 实现的平衡二叉堆结构。数据以队列形式存放在基础数组中。

父子节点索引关系

父节点:假设父节点为 `queue[n]`,那么:
  左子节点为 `queue[2n + 1]`
  右子节点为 `queue[2n + 2]`

父节点位置:任意孩子节点的父节点位置为 `(n - 1) >>> 1`,相当于减1后除2取整。

节点间大小关系

父节点:父节点的值小于或等于任意孩子节点的值。
同一层级的两个孩子节点:同一层级的两个孩子节点之间的大小关系不需要维护,它是在弹出元素时进行判断的。

子叶节点与非子叶节点

在一个长度为 `size` 的优先级队列中:
当 `index >= size >>> 1` 时,该节点为叶子节点。
否则,该节点为非叶子节点。

public class MyPriorityQueue<E> implements MyQueue<E> {
    // 默认容量,初始数组大小为10
    private static final int DEFAULT_CAPACITY = 10;
    // 队列中元素的数量
    private transient int size = 0;
    // 存储元素的数组,使用Object数组以支持泛型
    private transient Object[] elements;
    private transient Comparable<? super E> key;

    /**
     * 构造函数,初始化elements数组,默认容量为10。
     */
    public MyPriorityQueue() {
        elements = new Object[DEFAULT_CAPACITY];
    }

    /**
     * 返回队列中元素的数量。
     *
     * @return 队列中元素的数量
     */
    @Override
    public int size() {
        return size;
    }

    /**
     * 判断队列是否为空。
     *
     * @return 如果队列为空返回true,否则返回false
     */
    @Override
    public boolean isEmpty() {
        return size == 0;
    }

    /**
     * 添加元素到队列,等同于offer方法。
     *
     * @param e 要添加的元素
     * @return 如果添加成功返回true
     * @throws NullPointerException 如果元素为null
     */
    @Override
    public boolean add(E e) {
        return offer(e);
    }

    /**
     * 添加元素到队列,并返回是否添加成功。
     *
     * @param e 要添加的元素
     * @return 如果添加成功返回true
     * @throws NullPointerException 如果元素为null
     */
    @Override
    public boolean offer(E e) {
        // 检查元素是否为空
        if (e == null) {
            throw new NullPointerException();
        }
        // 如果当前容量足够,直接添加并调整堆
        if (size < elements.length) {
            elements[size++] = e;
            siftUpComparable(size - 1, e);
            return true;
        } else {
            // 容量不足时,扩容再添加
            grow(size + 1);
            elements[size++] = e;
            siftUpComparable(size - 1, e);
            return true;
        }
    }

    /**
     * 移除并返回队列头部的元素,队列不能为空。
     *
     * @return 队列头部的元素
     * @throws NoSuchElementException 如果队列为空
     */
    @Override
    public E remove() {
        if (size > 0) {
            E e = (E) elements[0];
            elements[0] = elements[--size];
            siftDownComparable(0, e);
            return e;
        }
        return null;
    }

    /**
     * 移除并返回队列头部的元素,队列为空时返回null。
     *
     * @return 队列头部的元素,如果队列为空返回null
     */
    @Override
    public E poll() {
        if (size > 0) {
            E e = (E) elements[0];
            int len = --size;
            E x = (E) elements[len];
            elements[len] = null;
            if (len > 0) {
                siftDownComparable(0, x);
            }
            return e;
        }
        return null;
    }

    /**
     * 查看队列头部的元素,不移除。
     *
     * @return 队列头部的元素,如果队列为空返回null
     */
    @Override
    public E peek() {
        return size > 0 ? (E) elements[0] : null;
    }

    /**
     * 打印当前队列的元素。
     */
    @Override
    public void printQueue() {
        System.out.println("当前队列:" + Arrays.toString(elements));
    }

    /**
     * 扩容方法,当元素数量超过当前数组容量时调用。
     *
     * @param minCapacity 最小容量
     */
    private void grow(int minCapacity) {
        int oldCapacity = elements.length;
        // 计算新容量,如果旧容量小于64,则增加2,否则增加旧容量的一半
        int newCapacity = oldCapacity + ((oldCapacity < 64) ? (oldCapacity + 2) : (oldCapacity >> 1));
        // 检查新容量是否超过Integer.MAX_VALUE - 8
        if (newCapacity - (Integer.MAX_VALUE - 8) > 0) {
            newCapacity = (minCapacity > (Integer.MAX_VALUE - 8) ? Integer.MAX_VALUE : (Integer.MAX_VALUE - 8));
        }
        // 复制数组到新的容量
        elements = Arrays.copyOf(elements, newCapacity);
    }

    /**
     * 将新添加的元素向上调整,以维持堆的性质。
     *
     * @param k 元素的索引
     * @param e 要调整的元素
     */
    @SuppressWarnings("unchecked")
    private void siftUpComparable(int k, E e) {
        key = (Comparable<? super E>) e;
        while (k > 0) {
            // 获取父节点index,相当于除以2, 相当于减1后除2取整
            int parent = (k - 1) >>> 1;
            System.out.println("入队——当前位置:" + k + ", 父节点位置:" + parent);
            Object o = elements[parent];
            // 如果当前元素不小于父元素,则位置合适,退出循环
            if (key.compareTo((E) o) >= 0) {
                System.out.println("入队——位置合适,退出循环");
                break;
            }
            // 否则,将父元素下移
            System.out.println("入队——位置不合适,将父节点下移");
            elements[k] = o;
            k = parent;
        }
        // 将新元素放在正确的位置
        elements[k] = key;
        System.out.println("调整后队列:" + Arrays.toString(elements));
    }

    /**
     * 将元素向下调整,以维持堆的性质。
     *
     * @param k 元素的索引
     * @param e 要调整的元素
     */
    @SuppressWarnings("unchecked")
    private void siftDownComparable(int k, E e) {
        key = (Comparable<? super E>) e;
        int half = size >>> 1;
        while (k < half) {
            // 找到左子节点和右子节点,两个节点进行比较,找出最大的值
            int child = (k << 1) + 1;
            Object c = elements[child];
            int right = child + 1;
            // 如果右子节点存在且小于左子节点,则选择右子节点
            if (right < size && ((Comparable<? super E>) c).compareTo((E) elements[right]) > 0) {
                System.out.println("出队——左右对比取小" + "left:" + c + "right:" + elements[right]);
                c = elements[child = right];
            }
            // 如果当前元素不大于子节点,则位置合适,退出循环
            if (key.compareTo((E) c) <= 0) {
                break;
            }
            // 否则,将子节点上移
            System.out.println("出队——上下对比" + "left:" + elements[k] + "right:" + c);
            elements[k] = c;
            k = child;
        }
        System.out.println("出队——最终位置" + "left:" + elements[k] + "right:" + e);
        // 将原元素放在正确的位置
        elements[k] = key;
    }
}

测试:

public class Test {
    public static void main(String[] args) {
        MyPriorityQueue<Integer> queue = new MyPriorityQueue<>();
        queue.offer(1);
        queue.offer(3);
        queue.offer(5);
        queue.offer(11);
        queue.offer(4);
        queue.offer(6);
        queue.offer(7);
        queue.offer(2);
        queue.printQueue();
        while (!queue.isEmpty()) {
            System.out.println(queue.poll());
        }
    }
}

测试结果:

调整后队列:[1, null, null, null, null, null, null, null, null, null]
入队——当前位置:1, 父节点位置:0
入队——位置合适,退出循环
调整后队列:[1, 3, null, null, null, null, null, null, null, null]
入队——当前位置:2, 父节点位置:0
入队——位置合适,退出循环
调整后队列:[1, 3, 5, null, null, null, null, null, null, null]
入队——当前位置:3, 父节点位置:1
入队——位置合适,退出循环
调整后队列:[1, 3, 5, 11, null, null, null, null, null, null]
入队——当前位置:4, 父节点位置:1
入队——位置合适,退出循环
调整后队列:[1, 3, 5, 11, 4, null, null, null, null, null]
入队——当前位置:5, 父节点位置:2
入队——位置合适,退出循环
调整后队列:[1, 3, 5, 11, 4, 6, null, null, null, null]
入队——当前位置:6, 父节点位置:2
入队——位置合适,退出循环
调整后队列:[1, 3, 5, 11, 4, 6, 7, null, null, null]
入队——当前位置:7, 父节点位置:3
入队——位置不合适,将父节点下移
入队——当前位置:3, 父节点位置:1
入队——位置不合适,将父节点下移
入队——当前位置:1, 父节点位置:0
入队——位置合适,退出循环
调整后队列:[1, 2, 5, 3, 4, 6, 7, 11, null, null]
当前队列:[1, 2, 5, 3, 4, 6, 7, 11, null, null]
出队——上下对比left:1right:2
出队——上下对比left:2right:3
出队——最终位置left:3right:11
1
出队——上下对比left:2right:3
出队——左右对比取小left:11right:4
出队——上下对比left:3right:4
出队——最终位置left:4right:7
2
出队——上下对比left:3right:4
出队——左右对比取小left:11right:7
出队——最终位置left:4right:6
3
出队——左右对比取小left:6right:5
出队——上下对比left:4right:5
出队——最终位置left:5right:7
4
出队——上下对比left:5right:6
出队——最终位置left:6right:11
5
出队——最终位置left:6right:7
6
出队——最终位置left:7right:11
7
11
    评论
    添加红包

    请填写红包祝福语或标题

    红包个数最小为10个

    红包金额最低5元

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

    打赏作者

    顾北辰20

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

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

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

    打赏作者

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

    抵扣说明:

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

    余额充值