在使用排序算法,除了使用快速排序解决问题,有时还可以用到优先队列,在《算法》这本书里,弄明白了优先队列,以及代码在此记录
优先队列定义
优先队列可以解决操作系统中cpu处理优先级进程的问题:
例如:小明在做菜在这是小明当前最高级别的进程,但是突然老妈喊他出来帮忙,处理老妈的进程就变为了比做菜更高一级的进程,那么这个时候就用到优先队列数据结构处理这样的一个变化。
优先队列来自于堆:
堆的特点
- 一棵完全二叉树(拥有完全二叉树的特点)
- 父节点总是大/小 于孩子节点(所以有大/小根堆)
- 根节点是最大/小值
堆有两个操作十分消耗时间分别是:
插入值:
插入的值根据完全二叉树性质,只能插入到第一个非叶子结点的孩子节点,插入完成后的节点可能不满足堆的要求(父节点值大于孩子节点)导致堆乱序,这个时将孩子节点值和父节点值进行交换,直到满足要求这个过程称为上浮(swim)
private void swim(int k ){
//数组二叉树的性质,孩子结点下标/2就是父亲结点
//孩子节点比父亲结点大就上浮
while (k > 1 && compare(pq,k/2,k)){
swap(pq,k/2,k);
k = k/2;
}
}
删除最大/最小值
删除最大/小值,这个时候堆的根节点被删除,整个堆都会散架,为了维持堆的结构完整又兼顾删除这个值,将根的值和最后一个节点值进行调换,将堆的最后一个元素抹掉就行heap[size--] = null
(这里null只要是抹掉就行,因为堆的size会-1)这个时候只有头结点值不符合堆性质,就需要下沉(sink)
private void sink(int k ){
//当前下标k的元素开始下沉
while (2*k <= size){
//孩子节点
int j = 2*k;
//左右孩子对比,尽量交换到大的节点
if (j < size && compare(pq,j,j+1)) j++;
//父节点不再小于孩子节点则退出
if (! compare(pq,k,j)) break;
swap(pq,j,k);
k = j ;
}
}
一个优先队列(默认大根堆)的实现
public class MaxPQ {
//数组构建堆
private int[] pq ;
//堆的大小
private int size = 0;
public MaxPQ(int[] array){
//初始化堆内存空间
pq = new int[1000];
//堆是从 1开始的
pq[0] = array[0];
//传入的数组进行堆构建
for (int i = 1 ; i <= array.length ; i++){
insert(array[i-1]);
}
}
public boolean isEmpty(){
return size == 0;
}
public int getSize(){
return size;
}
public void insert(int val){
//新元素必须为第一个非叶子结点的孩子
//同时堆的大小增加
pq[++size] = val;
//元素开始上浮
swim(size);
}
public int delMax(){
int max = pq[1];
swap(pq,1,size--);
pq[size+1] = 0;
sink(1);
// for (int i = 1 ; i <= size ; i++){
// System.out.print(pq[i]+" ");
// }
return max;
}
private void swap(int[] array, int a, int b) {
int tem = array[a];
array[a] = array[b];
array[b] = tem;
}
/**
* function annotation:
* 从下往上的堆有序化 (heapify)
*/
private void swim(int k ){
//数组二叉树的性质,孩子结点下标/2就是父亲结点
//孩子节点比父亲结点大就上浮
while (k > 1 && pq[k/2] < pq[k]){
swap(pq,k/2,k);
k = k/2;
}
}
/**
* function annotation:
* 从上往下的堆有序化
*/
private void sink(int k ){
//当前下标k的元素开始下沉
while (2*k <= size){
//孩子节点
int j = 2*k;
//左右孩子对比,尽量交换到大的节点
if (j < size && pq[j]<pq[j+1]) j++;
//父节点不再小于孩子节点则退出
if (! (pq[k] < pq[j])) break;
swap(pq,j,k);
k = j ;
}
}
//测试
// public static void main(String[] args) {
// int[] nums = new int[]{34,6,12,990,22,45,5};
// MaxPQ maxPQ = new MaxPQ(nums);
// LinkedList<Integer> stack = new LinkedList<>();
// for (int i = 0 ; i < nums.length ; i++){
// stack.push(maxPQ.delMax());
// }
// System.out.println(stack);
// }
}
结尾:这个代码有个缺点就是没有完成书本《算法》里面提到的在插入和删除的时候控制堆的空间大小这里默认开启了pq = new int[1000]
明显是很笨的做法浪费空间