Java优先级队列PriorityQueue
概述
优先级队列PriorityQueue是队列接口Queue的一种实现,能够以O(logn)的时间复杂度实现元素的插入和删除。PriorityQueue不是一个标准的队列,因为它保存元素的顺序不是按照元素入队的顺序,而一个是按元素的大小重新排序的,这已经违反了队列先进先出(FIFO)的基本规则。PriorityQueue是一种具有特殊用途的数据结构,它总能保持队首元素是队列中最大的,这个特性使得PriorityQueue在需要基于优先级处理任务的场景下非常有用,比如在调度算法、事件驱动编程、优先级排序等应用中。
想要每次都能从队列中获取最大元素通常能想到的方式有两种:比如元素无序保存,出队时遍历一遍队列找出最大元素,这种方式元素入队的时间复杂度为O(1),出队的时间复杂度为O(n);或者是元素按顺序保存,出队时直接取队首元素,这种方式元素入队的时间复杂度为O(n),出队的时间复杂度为O(1);由于这两种方式都不能兼顾元素入队出队的效率,于是便有了基于二叉堆的实现方式。
完全二叉堆
PriorityQueue内部实现通常采用堆数据结构,具体来说,是一个完全二叉堆,它在结构上是完全二叉树的形式,也就是说,除了最后一层可能不满外,每一层都是完全填充的,且最后一层的所有结点都尽可能地靠左排列。完全二叉堆分为两种类型:最大堆和最小堆。
最大堆:在最大堆中,每个父节点的值都大于或等于其子节点的值。因此,堆的根节点总是包含堆中的最大值。
最小堆:在最小堆中,每个父节点的值都小于或等于其子节点的值。所以,堆的根节点存储的是堆中的最小值。
完全二叉堆主要操作包括元素插入和删除:插入的新元素总是被添加到完全二叉树的最后一层的最右侧的空位置,然后通过上滤(swim)过程调整堆,以维护堆的性质。删除操作通常涉及移除堆顶元素,然后将堆的最后一个元素移动到堆顶位置填补空缺,并通过下滤(sink)过程调整堆。完全二叉堆由于其高效的特性(插入和删除O(logn)时间复杂度),常被用于实现优先队列等数据结构和各种算法中,比如堆排序、TopN问题等。堆的详细介绍请参考数据结构:堆
优先级队列的使用
PriorityQueue简单示例:
// 创建一个空的优先级队列
PriorityQueue<Integer> queue = new PriorityQueue<>();
// 添加元素
queue.offer(5);
queue.offer(3);
queue.offer(7);
queue.offer(1);
// 元素出队
System.out.println(queue.poll());
System.out.println(queue.poll());
System.out.println(queue.poll());
System.out.println(queue.poll());
输出为1、3、5、7。元素插入是无序的,输出按从小到大排序,可见这是一个最小堆实现的优先队列,也可以通过向构造器传入参数实现降序排序。
定制排序:
// 创建一个空的优先级队列并传入排序规则
PriorityQueue<Integer> queue = new PriorityQueue<>(Comparator.reverseOrder());
// 添加元素
queue.offer(5);
queue.offer(3);
queue.offer(7);
queue.offer(1);
// 元素出队
System.out.println(queue.poll());
System.out.println(queue.poll());
System.out.println(queue.poll());
System.out.println(queue.poll());
//输出为7、5、3、1
使用集合初始化优先队列:
//创建一个优先级队列并使用List初始化
PriorityQueue<Integer> queue1 = new PriorityQueue<>(Arrays.asList(3,1,7,5));
//创建一个优先级队列并使用另一个队列初始化
PriorityQueue<Integer> queue2 = new PriorityQueue<>(queue1);
//创建一个优先级队列并使用set集合初始化
Set<Integer> set = new HashSet<>();
PriorityQueue<Integer> queue3 = new PriorityQueue<>(set);
Java标准库中实现了多种PriorityQueue的构造器,能够通过不同类型的集合快速地生成一个优先级队列,为程序员提供了便利,提升了应用开发的灵活性。
总结
PriorityQueue不遵循传统队列的FIFO原则,作为一种特殊的队列类型,它通过动态调整元素顺序,高效地支持了基于优先级的操作,是解决特定类型问题的强大工具。