堆(优先队列)
时间复杂度:
初始化建堆的时间复杂度为 O(n)
堆排序重建堆 O(nlogn)
PriorityQueue小顶堆
小顶堆:任意一个非叶子节点的权值,都不大于其左右子节点的权值
构造函数
构造器 | 功能介绍 |
---|---|
PriorityQueue() | 创建一个空的优先级队列,默认容量是11 |
PriorityQueue(int initialCapacity) | 创建一个初始容量为initialCapacity的优先级队列,注意: initialCapacity不能小于1,否则会抛 IllegalArgumentException异常 |
PriorityQueue(Collection c) | 用一个集合来创建优先级队列 |
也可以在初始化的时候指定comparator接口的实现,
常用api
函数名 | 功能介绍 |
---|---|
boolean offer(E e) | 插入元素e,插入成功返回true,如果e对象为空,抛出NullPointerException 异常,时间复杂度 O ( l o g 2 N ) O(log_2N) O(log2N),注意:空间不够时候会进行扩容 |
E peek() | 获取优先级最高的元素,如果优先级队列为空,返回null |
E poll() | 移除优先级最高的元素并返回,如果优先级队列为空,返回null |
int size() | 获取有效元素的个数 |
void clear() | 清空 boolean |
isEmpty() | 检测优先级队列是否为空,空返回true |
特性:
-
PriorityQueue是线程不安全的,PriorityBlockingQueue是线程安全的
-
PriorityQueue中存储的对象必须要能够比较大小,如果不能比较大小就会抛出ClassCaseException异常.
-
不能插入null对象,否则抛出NullPointerException
-
带有自动扩容机制
-
插入和杀出的时间复杂度为
O ( l o g 2 N ) O(log_2N) O(log2N) -
底层使用了堆
扩容的过程 jdk1.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);
}
从扩容方法上可以看出
- 如果容量小于64使用oldCapacity+oldCapacity+2的方式
- 如果容量大于64使用oldCapacity+(oldCapacity>>1)的方式(相当于1.5倍扩容)
底层数据结构
堆的概念
将一个关键码的集合k={k,k1,k2,k3…kn-1}把它的所有元素按照完全二叉树的顺序方式存储在数组中
在一个一维数组中
- 满足ki<=k2i+1 且 ki<=k2i+2的称为小堆
- 满足ki>=k2i+1 且 ki>=k2i+2的称为大堆
将根节点最大的堆叫做最大堆或大根堆,根节点最小的堆叫做最小堆或小根堆。
性质:
- 堆中某个节点的值总是不大于或者不小于父节点的值
- 堆总是一个完全二叉树
- 如果i为0,则i表示的节点为根节点,否则i节点的双亲为(i-1)/2
- 如果2*i+1小于节点个数,则节点i的左孩子下标为2*i+1,否则没有左孩子
- 如果2*i+2小于节点个数,则节点i的右孩子下标为2*i+2,否则没有右孩子
向上调整
向上调整的过程(最小堆)
- 先设定倒数第一个叶子节点为当前节点,标记为cur,找出他的父节点,用parent来标记
- 比较paren和cur的值,如果cur比parent小.则不满足小顶堆的规则,需要进行交换. 如果cur比parent大就不交换,此时调整结束.
- 如果不满足条件交换后, cur就重设为父节点下标,重新开始循环.然后检测是否满足最小堆的性质,一直到满足条件或者cur小于等于0结束.
向下调整
- 首先设定根节点为当前节点标记为cur,比较左右子树的值,找出更小的值,用child来标记.
- 比较child和cur的值,如果child比cur小,则不满足小堆的规则,需要进行交换.如果cur比parent大就不交换.此时调整结束
- 如果不满足条件交换后, cur就重设为子节点下标,重新开始循环.然后检测是否满足最小堆的性质,一直到满足条件或者cur小于等于0结束.
堆的创建(向上调整)
当有了个一个数组,但是不满足堆的结构要求,我们要从倒数第一个非叶子节点的子树开始调整,一直到根节点的树.
ArrayList<Integer> integers = new ArrayList<>();
integers.add(95);
integers.add(9);
integers.add(14);
integers.add(48);
integers.add(29);