赞
踩
1. 使用时必须导入PriorityQueue所在的包,即:
import java.util.PriorityQueue;
2. PriorityQueue中放置的元素必须要能够比较大小,不能插入无法比较大小的对象,否则会抛出
ClassCastException异常
3. 不能插入null对象,否则会抛出NullPointerException
4. 没有容量限制,可以插入任意多个元素,其内部可以自动扩容
5. 插入和删除元素的时间复杂度为
6. PriorityQueue底层使用了堆数据结构, (注意:此处大家可以不用管什么是堆,后文中有介绍)
7. PriorityQueue默认情况下是小堆---即每次获取到的元素都是最小的元素
- static void TestPriorityQueue(){
- // 创建一个空的优先级队列,底层默认容量是11
- PriorityQueue<Integer> q1 = new PriorityQueue<>();
- // 创建一个空的优先级队列,底层的容量为initialCapacity
- PriorityQueue<Integer> q2 = new PriorityQueue<>(100);
- ArrayList<Integer> list = new ArrayList<>();
- list.add(4);
- list.add(3);
- list.add(2);
- list.add(1);
- // 用ArrayList对象来构造一个优先级队列的对象
- // q3中已经包含了三个元素
- PriorityQueue<Integer> q3 = new PriorityQueue<>(list);
- System.out.println(q3.size());
- System.out.println(q3.peek());
- }
注意:默认情况下,PriorityQueue队列是小堆,如果需要大堆需要用户提供比较器
- // 用户自己定义的比较器:直接实现Comparator接口,然后重写该接口中的compare方法即可
- class IntCmp implements Comparator<Integer>{
- @Override
- public int compare(Integer o1, Integer o2) {
- return o2-o1;
- }
- }
- public class TestPriorityQueue {
- public static void main(String[] args) {
- PriorityQueue<Integer> p = new PriorityQueue<>(new IntCmp());
- p.offer(4);
- p.offer(3);
- p.offer(2);
- p.offer(1);
- p.offer(5);
- System.out.println(p.peek());
- }
- }
- static void TestPriorityQueue2(){
- int[] arr = {4,1,9,2,8,0,7,3,6,5};
- // 一般在创建优先级队列对象时,如果知道元素个数,建议就直接将底层容量给好
- // 否则在插入时需要不多的扩容
- // 扩容机制:开辟更大的空间,拷贝元素,这样效率会比较低
- PriorityQueue<Integer> q = new PriorityQueue<>(arr.length);
- for (int e: arr) {
- q.offer(e);
- } S
- ystem.out.println(q.size()); // 打印优先级队列中有效元素个数
- System.out.println(q.peek()); // 获取优先级最高的元素
- // 从优先级队列中删除两个元素之和,再次获取优先级最高的元素
- q.poll();
- q.poll();
- System.out.println(q.size()); // 打印优先级队列中有效元素个数
- System.out.println(q.peek()); // 获取优先级最高的元素
- q.offer(0);
- System.out.println(q.peek()); // 获取优先级最高的元素
- // 将优先级队列中的有效元素删除掉,检测其是否为空
- q.clear();
- if(q.isEmpty()){
- System.out.println("优先级队列已经为空!!!");
- } e
- lse{
- System.out.println("优先级队列不为空");
- }
- }
注意:以下是JDK 1.8中,PriorityQueue的扩容方式
- private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
- private void grow(int minCapacity) {
- int oldCapacity = queue.length;
- // Double size if small; else grow by 50%
- int newCapacity = oldCapacity + ((oldCapacity < 64) ?
- (oldCapacity + 2) :
- (oldCapacity >> 1));
- // overflow-conscious code
- if (newCapacity - MAX_ARRAY_SIZE > 0)
- newCapacity = hugeCapacity(minCapacity);
- queue = Arrays.copyOf(queue, newCapacity);
- }
- private static int hugeCapacity(int minCapacity) {
- if (minCapacity < 0) // overflow
- throw new OutOfMemoryError();
- return (minCapacity > MAX_ARRAY_SIZE) ?
- Integer.MAX_VALUE :
- MAX_ARRAY_SIZE;
- }
- class Solution {
- public int[] smallestK(int[] arr, int k) {
- // 参数检测
- if(null == arr || k <= 0)
- return new int[0];
- PriorityQueue<Integer> q = new PriorityQueue<>(arr.length);
- // 将数组中的元素依次放到堆中
- for(int i = 0; i < arr.length; ++i){
- q.offer(arr[i]);
- } /
- / 将优先级队列的前k个元素放到数组中
- int[] ret = new int[k];
- for(int i = 0; i < k; ++i){
- ret[i] = q.poll();
- } r
- eturn ret;
- }
- }
2.2 堆的存储方式
从堆的概念可知,堆是一棵完全二叉树,因此可以层序的规则采用顺序的方式来高效存储,
注意:对于非完全二叉树,则不适合使用顺序方式进行存储,因为为了能够还原二叉树,空间中必须要存储空节
点,就会导致空间利用率比较低。
将元素存储到数组中后,可以根据二叉树章节的性质5对树进行还原。假设i为节点在数组中的下标,则有:
如果i为0,则i表示的节点为根节点,否则i节点的双亲节点为 (i - 1)/2
如果2 * i + 1 小于节点个数,则节点i的左孩子下标为2 * i + 1,否则没有左孩子
如果2 * i + 2 小于节点个数,则节点i的右孩子下标为2 * i + 2,否则没有右孩子
例题
- 1.下列关键字序列为堆的是:()
- A: 100,60,70,50,32,65 B: 60,70,65,50,32,100 C: 65,100,70,32,50,60
- D: 70,65,100,32,50,60 E: 32,50,100,70,65,60 F: 50,100,70,65,60,32
- 2.已知小根堆为8,15,10,21,34,16,12,删除关键字8之后需重建堆,在此过程中,关键字之间的比较次数是()
- A: 1 B: 2 C: 3 D: 4
- 3.一组记录排序码为(5 11 7 2 3 17),则利用堆排序方法建立的初始堆为()
- A: (11 5 7 2 3 17) B: (11 5 7 2 17 3) C: (17 11 7 2 3 5)
- D: (17 11 7 5 3 2) E: (17 7 11 3 5 2) F: (17 7 11 3 2 5)
- 4.最小堆[0,3,2,5,7,4,6,8],在删除堆顶元素0之后,其结果是()
- A: [3,2,5,7,4,6,8] B: [2,3,5,7,4,6,8]
- C: [2,3,4,5,7,8,6] D: [2,3,4,5,6,7,8]
- [参考答案]
- 1.A 2.C 3.C 4.C
2.3 堆的创建
2.3.1 堆向下调整
对于集合{ 27,15,19,18,28,34,65,49,25,37 }中的数据,如果将其创建成堆呢?
仔细观察上图后发现:根节点的左右子树已经完全满足堆的性质,因此只需将根节点向下调整好即可。
向下过程(以小堆为例):
1. 让parent标记需要调整的节点,child标记parent的左孩子(注意:parent如果有孩子一定先是有左孩子)
2. 如果parent的左孩子存在,即:child < size, 进行以下操作,直到parent的左孩子不存在
parent右孩子是否存在,存在找到左右孩子中最小的孩子,让child进行标
将parent与较小的孩子child比较,如果:
parent小于较小的孩子child,调整结束
否则:交换parent与较小的孩子child,交换完成之后,parent中大的元素向下移动,可能导致子
树不满足对的性质,因此需要继续向下调整,即parent = child;child = parent*2+1; 然后继续2
- public void shiftDown(int[] array, int parent) {
- // child先标记parent的左孩子,因为parent可能右左没有右
- int child = 2 * parent + 1;
- int size = array.length;
- while (child < size) {
- // 如果右孩子存在,找到左右孩子中较小的孩子,用child进行标记
- if(child+1 < size && array[child+1] < array[child]){
- child += 1;
- } /
- / 如果双亲比其最小的孩子还小,说明该结构已经满足堆的特性了
- if (array[parent] <= array[child]) {
- break;
- }else{
- // 将双亲与较小的孩子交换
- int t = array[parent];
- array[parent] = array[child];
- array[child] = t;
- // parent中大的元素往下移动,可能会造成子树不满足堆的性质,因此需要继续向下调整
- parent = child;
- child = parent * 2 + 1;
- }
- }
- }
注意:在调整以parent为根的二叉树时,必须要满足parent的左子树和右子树已经是堆了才可以向下调整。
时间复杂度分析:
最坏的情况即图示的情况,从根一路比较到叶子,比较的次数为完全二叉树的高度,即时间复杂度为Olog2n
2.3.2 堆的创建
那对于普通的序列{ 1,5,3,8,7,6 },即根节点的左右子树不满足堆的特性,又该如何调整呢
- public static void createHeap(int[] array) {
- // 找倒数第一个非叶子节点,从该节点位置开始往前一直到根节点,遇到一个节点,应用向下调整
- int root = ((array.length-2)>>1);
- for (; root >= 0; root--) {
- shiftDown(array, root);
- }
- }
2.3.3 建堆的时间复杂度
因为堆是完全二叉树,而满二叉树也是完全二叉树,此处为了简化使用满二叉树来证明(时间复杂度本来看的就是
近似值,多几个节点不影响最终结果):
因此:建堆的时间复杂度为O(N)
- public void shiftUp(int child) {
- // 找到child的双亲
- int parent = (child - 1) / 2;
- while (child > 0) {
- // 如果双亲比孩子大,parent满足堆的性质,调整结束
- if (array[parent] > array[child]) {
- break;
- } e
- lse{
- // 将双亲与孩子节点进行交换
- int t = array[parent];
- array[parent] = array[child];
- array[child] = t;
- // 小的元素向下移动,可能到值子树不满足对的性质,因此需要继续向上调增
- child = parent;
- parent = (child - 1) / 1;
- }
- }
- }
- public class MyPriorityQueue {
- // 演示作用,不再考虑扩容部分的代码
- private int[] array = new int[100];
- private int size = 0;
- public void offer(int e) {
- array[size++] = e;
- shiftUp(size - 1);
- }
- public int poll() {
- int oldValue = array[0];
- array[0] = array[--size];
- shiftDown(0);
- return oldValue;
- }
- public int peek() {
- return array[0];
- }
- }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。