1、优先队列的特点
队列 的特点是 先进先出, 入队列,将新元素置于队尾:
出队列,队头元素最先被移出:
优先队列不再遵循先入先出的原则,而是分为两种情况:
- 最大优先队列,无论入队顺序如何,都是当前最大的元素优先出队
- 最小优先队列,无论入队顺序如何,都是当前最小的元素优先出队
例:
有一个最大优先队列,其中的最大元素是8,那么虽然8并不是队头元素,
但出队时仍然让元素8首先出队
2、优先队列的实现
二叉堆的特性:
- 最大堆的堆顶是整个堆中的最大元素
- 最小堆的堆顶是整个堆中的最小元素
可以用最大堆来实现最大优先队列:
每一次 入队 操作就是堆的 插入 操作;
每一次 出队 操作就是删除堆顶节点
入队操作 步骤如下:
- 插入新节点5
- 新节点5“上浮”到合适位置
出队操作 步骤如下: - 让原堆顶节点10出队
- 把最后一个节点1替换到堆顶位置
- 节点1“下沉”,节点9成为新堆顶
二叉堆节点“上浮”和“下沉”的时间复杂度都是 O(logn)
所以优先队列入队和出队的时间复杂度也是 O(logn)
3、优先队列的代码实现
采用数组来存储二叉堆的元素,当元素数量超过数组长度时,需要进行扩容来扩大数组长度
import java.util.Arrays;
//构建队列
public class TreeNode4 {
private int[] array;
private int size;
public TreeNode4(){
//队列初始长度为32
array = new int[32];
}
/**
* “入队”操作
* @param key 入队元素
*/
public void enQueue(int key) {
//队列长度超出范围,扩容
if(size >= array.length){
resize();
}
array[size++] = key;
upAdjust();
}
/**
* “出队”操作
*/
public int deQueue() throws Exception {
if(size <=0){
throw new Exception("The queue is empty!");
}
//获得栈顶元素
int head = array[0];
//让最后一个元素移动到栈顶
array[0] = array[--size];
downAdjust();
return head;
}
/**
* “上浮操作”
*/
public void upAdjust() {
int childIndex = size - 1;
int parentIndex = (childIndex-1)/2;
//temp :保存插入的叶子节点值,用于最后的赋值
int temp = array[childIndex];
while (childIndex > 0 && temp >array[parentIndex]){
//无需真正交换,单项赋值即可
array[childIndex] = array[parentIndex];
childIndex = parentIndex;
parentIndex = parentIndex/2;
}
array[childIndex] = temp;
}
/**
* “下沉操作”
*/
public void downAdjust() {
//temp :保存父节点的值,用于最后的赋值
int parentIndex = 0;
int temp = array[parentIndex];
int childIndex = 1;
while (childIndex < size){
//如果有右孩子,且右孩子大于左孩子的值,则定位到右孩子
if(childIndex +1 <size && array[childIndex +1]>array[childIndex]){
childIndex++;
}
//如果父节点大于任何一个孩子的值,直接跳出
if(temp >= array[childIndex]){
break;
}
//无须真正交换,单向赋值即可
array[parentIndex] = array[childIndex];
parentIndex = childIndex;
childIndex = 2 * childIndex + 1;
}
array[parentIndex] = temp;
}
/**
* “队列扩容”
*/
public void resize() {
//队列容量翻倍
int newSize = this.size * 2;
this.array = Arrays.copyOf(this.array,newSize);
}
public static void main(String[] args) throws Exception{
TreeNode4 priorityQueue = new TreeNode4();
priorityQueue.enQueue(3);
priorityQueue.enQueue(5);
priorityQueue.enQueue(10);
priorityQueue.enQueue(2);
priorityQueue.enQueue(7);
System.out.println("出队元素:" + priorityQueue.deQueue());
System.out.println("出队元素2:" + priorityQueue.deQueue());
}
}
编译输出:
4、总结
- 树
树是n个节点的有限集,有且仅有一个特定的称为根的节点。当n>1时,其余节 点可分为m个互不相交的有限集,每一个集合本身又是一个树,并称为根的子树; - 二叉树
二叉树是树的一种特殊形式,每一个节点最多有两个孩子节点。二叉树包含完
全二叉树和满二叉树两种特殊形式; - 二叉树的遍历方式
根据遍历节点之间的关系,可以分为前序遍历、中序遍历、后序遍历、层序遍历这4种方式;
从更宏观的角度划分,可以划分为深度优先遍历和广度优先遍历两大类; - 二叉堆
二叉堆是一种特殊的完全二叉树,分为最大堆和最小堆:
在最大堆中,任何一个父节点的值,都大于或等于它左、右孩子节点的值。
在最小堆中,任何一个父节点的值,都小于或等于它左、右孩子节点的值。 - 优先队列
优先队列分为最大优先队列和最小优先队列;
在最大优先队列中,无论入队顺序如何,当前最大的元素都会优先出队,这是 基于最大堆实现的;
在最小优先队列中,无论入队顺序如何,当前最小的元素都会优先出队,这是 基于最小堆实现的。
———————————————————————————————————————
内容来源:《漫画算法》
关注公众号,回复 【算法】,获取高清算法书!