第五节:Java集合框架之优先级队列PriorityQueue(堆)
Posted 快乐江湖
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了第五节:Java集合框架之优先级队列PriorityQueue(堆)相关的知识,希望对你有一定的参考价值。
文章目录
- Java集合框架中
PriorityQueue
底层使用堆实现,所以本文先介绍堆的概念和实现
一:堆基本概念
(1)什么是堆
堆:堆就是一颗完全二叉树,这颗完全二叉树有这样一个特点:它的结点要么大于任意一个孩子结点,要么小于任意一个孩子结点
- 如果其结点大于任意一个孩子结点,就称其为大顶堆,此时其最大的结点是根结点
- 如果其结点小于任意一个孩子结点,就称其为小顶堆,此时其最小的结点是根结点
(2)堆存储方式
堆存储方式:堆是一颗完全二叉树,所以可以利用一个数组来存储,数组元素顺序正好对应二叉树的层次遍历序列
一旦将堆中的元素存储到数组后,根据二叉树性质就会有以下结论,假设 i i i为某结点在数组中的下标
- 如果 i i i为0,则代表根结点,否则 i i i结点的双亲结点为 i − 1 2 \\fraci-12 2i−1
- 如果 2 i + 1 2i+1 2i+1<结点个数,则结点 i i i的左孩子下标为 2 i + 1 2i+1 2i+1,否则没有左孩子
- 如果 2 i + 1 2i+1 2i+1>结点个数,则结点 i i i的右孩子下标为 2 i + 1 2i+1 2i+1,否则没有右孩子
二:堆的模拟实现
- 以创建小顶堆为例
(1)重点操作说明
A:堆的初始化
堆的初始化:程序开始时会给出一个数组,这个数组存储了某个完全二叉树的层次遍历结果,但此时它并不是堆,如下
- 给定数组【27, 15, 19, 18, 28, 34, 65, 49, 25, 37】
B:堆的向下调整
堆的向下调整:上面的完全二叉树并不是堆,所以需要进行向下调整,把较小的结点换到上面去
下图展示了一次调整过程
下图是动态调整过程
C:堆的构造
上面堆的向下调整算法要求左右子树必须是堆,但一般不会有这样的理想情况。那么为什么还要讲堆的向下调整算法呢?其实对于一个完全二叉树,从根节点角度看,确实满足不了情况。但是,去考察极限情况——一个结点就是一个完全二叉树,它也一定是一个堆,所以可以从最后一个非叶结点开始(也可以从最后一个结点开始,只不过,如果从最后一个结点开始效率不高),对每一个结点使用堆的向下调整,直到根节点,这样就能构造一个小顶堆了。如果一个完全二叉树有n个结点,那么它的最后一个非叶结点的编号为 n 2 − 1 \\fracn2-1 2n−1
D:堆的插入
插入元素时,新元素的到来很可能会破坏当前堆的结构,所以可以重新建立堆。而与向下调整算法相反,堆插入时使用的是向上调整算法。如下
E:堆的删除
堆删除元素默认删除第一个,因为删除其它元素没有意义。删除时,将最后一个元素与第一个元素交换,然后对新的根进行向下调整即可
(2)代码
package myHeap;
import java.util.Arrays;
public class MyHeap
public int[] elem;
public int usedSize;//当前堆中有效数据个数
public MyHeap()
this.elem = new int[10];
this.usedSize = 0;
//传入数组
public void initArray(int[] array)
this.elem = Arrays.copyOf(array, array.length);
this.usedSize = this.elem.length;
//交换
private void swap(int[]array, int i, int j)
int temp = array[i];
array[i] = array[j];
array[j] = temp;
//向下调整算法
private void adjustDown(int parent, int len)
int child = 2 * parent + 1;
while(child < len)
//建立小顶堆,那么就要找到更小的
//注意child+1可能越界
if(child+1 < usedSize && this.elem[child+1] < this.elem[child])
child++;
//如果孩子更小,那么交换
if(this.elem[child] < this.elem[parent])
//交互完成后,继续向下走
swap(this.elem, child, parent);
parent = child;
child = 2 * parent + 1;
else
break;
//向上调整算法
private void adjustUp(int child)
int parent = (child-1) / 2;
while(child > 0)
if(this.elem[child] < this.elem[parent])
swap(elem, child, parent);
child = parent;
parent = (child-1) / 2;
else
break;
//显示
public void display()
System.out.println(Arrays.toString(this.elem));
//堆的建立(以小顶堆为例)
public void createHeap()
//从最后一个非叶结点开始依次向下调整
for(int parent = (usedSize-1-1)/2; parent>=0; parent--)
adjustDown(parent, usedSize);
//堆的插入
public void insert(int val)
if(isFull())
this.elem = Arrays.copyOf(this.elem, this.elem.length*2);
this.elem[usedSize] = val;
usedSize++;
adjustUp(usedSize-1);
//堆的删除
public int remove()
if(isEmpty())
return -1;
int retVal = this.elem[0];
swap(this.elem, 0, usedSize-1);
usedSize--;
adjustDown(0 ,usedSize);
return retVal;
//判满
public boolean isFull()
return this.usedSize == this.elem.length;
//判空
public boolean isEmpty()
return this.usedSize == 0;
三:PriorityQueue使用
(1)基本使用
PriorityQueue:PriorityQueue是指优先级队列,底层使用堆实现
- Java集合框架中有
PriorityQueue
(线程不安全)和PriorityBlockingQueue
(线程安全)这两种优先级队列
使用时注意
PriorityQueue
中放置的元素必须能够比较大小,否则会抛出ClassCastException
异常- 不要插入
null
对象,否则抛出NullPointerException
PriorityQueue
默认建立小顶堆,如果想要创建大顶堆则用户需要提供比较器
涉及方法如下
boolean offer(E, e)
:插入元素e
E peek()
:获取优先级最高的元素E poll()
:移除优先级最高的元素并返回int size()
:获取有效元素个数void clear()
:清空boolean isEmpty()
:判空
下面是一个例子
import java.util.PriorityQueue;
public class TestDemo
public static void main(String[] args)
int[] arr = 4, 1, 9, 2, 8, 9, 9, 7, 3, 6 ,5;
PriorityQueue<Integer> priorityQueue = new PriorityQueue<>();
for(int e : arr)
priorityQueue.offer(e);
System.out.println("打印优先级队列" + priorityQueue);
System.out.println("打印优先级队列中有效元素个数" + priorityQueue.size());
System.out.println("获取优先级最高的元素" + priorityQueue.peek());
priorityQueue.poll();
System.out.println("删除两个优先级最高的元素然后再次获取" + priorityQueue.peek());
System.out.println("判断优先级队列是否为空" + priorityQueue.isEmpty());
priorityQueue.clear();
System.out.println("清空优先级队列并判空" + priorityQueue.isEmpty());
(2)说明
前面说过, PriorityQueue
中放置的元素必须能够比较大小,否则会抛出ClassCastException
异常。因此在存放一些自定义数据类型时,该数据类型需要实现Comparable
接口,如下
import java.util.PriorityQueue;
class People implements Comparable<People>
public int age;
public People(int age)
this.age = age;
@Override
public int compareTo(People p)
return this.age - p.age;
public class TestDemo
public static void main(String[] args)
PriorityQueue<People> priorityQueue = new PriorityQueue<>();
priorityQueue.offer(new People(13));
priorityQueue.offer(new People(27));
priorityQueue.offer(new People(99));
priorityQueue.offer(new People(19));
priorityQueue.offer(new People(7));
System.out.println("最小年龄是:" + priorityQueue.peek().age + "岁");
以上是关于第五节:Java集合框架之优先级队列PriorityQueue(堆)的主要内容,如果未能解决你的问题,请参考以下文章