一、前情提要
Java
集合框架中提供了
PriorityQueue
和
PriorityBlockingQueue
两种类型的优先级队列,
PriorityQueue
是线
程不安全的,
PriorityBlockingQueue
是线程安全的
,本文主要介绍的是对于
PriorityQueue的代码模拟实现
。(ps:为了方便大家理解,这里我推荐一个算法动画视频,对于理解下面代码很有帮助,下面代码也是算是对于动画的实践或补充吧。点这->算法动画视频)
二、具体代码实现
(1)定义优先级队列,包括成员变量和构造方法:
public class PriorityQueue {
private int[] elem; // 存储堆元素的数组
private int usedSize; // 记录当前堆中元素个数
private static final int DEFAULT_CAPACITY = 10; // 默认初始容量
public PriorityQueue() {
this.elem = new int[DEFAULT_CAPACITY];
this.usedSize = 0;
}
(2)建堆
/**
* 建堆:从最后一个非叶子节点开始向下调整
* 时间复杂度:O(n)
* @param array 传入的无序数组
*/
public void createHeap(int[] array) {
if (array == null || array.length == 0) return;
this.elem = new int[array.length];
System.arraycopy(array, 0, this.elem, 0, array.length);
this.usedSize = array.length;
for (int i = (usedSize - 2) / 2; i >= 0; i--) {
shiftDown(i, usedSize);
}
}
(3)向下调整
/**
* 向下调整(维持堆的性质)
* 时间复杂度:O(log n)
*/
private void shiftDown(int root, int len) {
int parent = root;
int child = 2 * parent + 1; // 左孩子
while (child < len) {
// 选择较大的子节点
if (child + 1 < len && elem[child + 1] > elem[child]) {
child++;
}
if (elem[parent] >= elem[child]) {
break;
}
swap(parent, child);
parent = child;
child = 2 * parent + 1;
}
}
(4)插入元素
/**
* 插入元素
* 先插入到数组末尾,然后向上调整
*/
public void push(int val) {
if (isFull()) {
resize();
}
elem[usedSize] = val;
shiftUp(usedSize);
usedSize++;
}
(5)向上调整
/**
* 向上调整(插入后保证堆的性质)
* 时间复杂度:O(log n)
*/
private void shiftUp(int child) {
int parent = (child - 1) / 2;
while (child > 0 && elem[child] > elem[parent]) {
swap(child, parent);
child = parent;
parent = (child - 1) / 2;
}
}
(6)取出堆顶元素
/**
* 取出堆顶元素(删除最大元素)
* 用最后一个元素替换堆顶,然后向下调整
*/
public void pollHeap() {
if (isEmpty()) {
throw new RuntimeException("Priority Queue is empty");
}
swap(0, usedSize - 1);
usedSize--;
shiftDown(0, usedSize);
}
(7)获取堆顶元素
/**
* 获取堆顶元素(最大值)
*/
public int peekHeap() {
if (isEmpty()) {
throw new RuntimeException("Priority Queue is empty");
}
return elem[0];
}
(8)空满合法性判断
/**
* 判断堆是否已满
*/
public boolean isFull() {
return usedSize == elem.length;
}
/**
* 判断堆是否为空
*/
public boolean isEmpty() {
return usedSize == 0;
}
(9)数组扩容
/**
* 数组扩容(动态扩展)
*/
private void resize() {
int newCapacity = elem.length * 2;
int[] newArray = new int[newCapacity];
System.arraycopy(elem, 0, newArray, 0, elem.length);
elem = newArray;
}
(10)交换数组两个元素
/**
* 交换数组中两个元素
*/
private void swap(int i, int j) {
int temp = elem[i];
elem[i] = elem[j];
elem[j] = temp;
}
三、补充
上述代码中,我都在注释中写明了时间复杂度,其中有一个可能大家很难理解,那就是建堆的复杂度,我这边给大家手写推一下,以下为推导过程:
因为堆是完全二叉树,而满二叉树也是完全二叉树,此处为了简化使用满二叉树来证明
(
时间复杂度本来看的就是近似值,多几个节点不影响最终结果)
:
因此建堆的时间复杂度为O(n)。
感谢你能看到这,希望对你有所帮助,可以的话点个赞吧,感恩~

被折叠的 条评论
为什么被折叠?



