优先队列
优先队列
二叉堆
本节将二叉堆之称为堆,就像二叉查找树一样,堆也有两个性质,即结构性和堆序性。恰似AVL树,对堆的一次操作可能破坏这两个性质中的一个,因此,堆的一次操作必须到堆的所有性质都被满足才能终止。
结构性质
堆是一棵完全被填满的二叉树,有可能的例外是在底层,底层上的元素从左到右填入,这样的树称为完全二叉树(complete binary tree)。

图 1 一棵完全二叉树
容易证明,一棵高为h的完全二叉树有2h到2(h+1)-1个节点。这意味着完全二叉树的高是O(logN)。因为完全二叉树的规律,所以它可以用一个数组表示而不需要使用链。
对于数组中任一位置i上的元素,其左儿子在位置2i上,右儿子在左儿子后的单元(2i+1)中,它的父亲则在位置[i/2]上。因此这里不仅不需要链,而且遍历该树所需要的操作极简单,在大部分计算机上运行可能非常快。这种实现方法的唯一问题在于,最大的堆大小需要事先估计,但一般这并不成问题(而且如果需要,我们可以重新调整大小)。
优先队列的应用
选择问题
假设只考虑找出第k个最小的元素。该算法很简单,我们将N个元素读入一个数组。然后对该数组应用buildHeap算法。最后,执行K次deleteMin操作。从该堆最后提取的元素就是我们的答案。显然,只要改变堆序性质,也可以找出第k个最大元素。
什么是优先队列
普通的队列都是先进先出的形式,元素在队尾加入,在队头删除。优先队列中,元素被赋予优先级,具有最高优先级的元素先出。通常采用二叉堆结构实现。
优先队列和堆一样,有两种形式:最大优先队列和最小优先队列。
对于每次的push和pop操作,队列都会动态的调整
Java中的PriorityQueue类实现优先队列
常用方法
public boolean add(E e); //在队尾添加元素
public E peek(); //取出队头元素
public boolean remove(Object o); //删除指定元素
public boolean contains(Object o); //检查是否包含元素
public Object[] toArray(); //将队列转换为数组
public int size(); //返回元素个数
public void clear(); //清空队列
public E poll(); //返回并清空队头元素
public boolean isEmpty(); //判断队列是否为空
最小优先队列
public class test{
@test
public void test(){
int[] nums = new int[]{12,3,8,5,16,30,9};
Queue<Integer> queue = new PriorityQueue<>();
for(int num:nums){
queue.add(num);
}
System.out.println(Arrays.toString(queue.toArray()));
while(!queue.isEmpty()){
System.out.print(queue.poll() + " ");
}
}
}
最大优先队列
public class test{
@Test
public void test(){
int[] nums = new int[] {12,3,8,5,16,30,9};
Queue<Integer> queue = new PriorityQueue<>(new Comparator<Integer>()){
@override
public int compare(Integer o1,Integer o2){
return o2 - o1;
}
});
for(int num:nums){
queue.add(num);
}
System.out.println(Arrays.toString(queue.toArray()));
while(!queue.isEmpty()){
System.out.print(queue.poll() + " ");
}
}
}
}
堆排序
堆排序的基本思想,将待排序序列构造成一个大顶堆,此时,整个序列的最大值就是堆顶的根节点。将其与末尾元素进行交换,此时末尾就成为最大值。然后将剩余的n-1个元素重新构造成一个堆,这样就会得到n个元素的次小值。如此反复执行,就能得到一个有序序列。
步骤:
- 将无序序列构建成一个堆,根据升降需求选择大顶堆或者小顶堆;
- 将堆元素与末尾元素交换,将最大元素沉到数组末端;
- 重新调整结构,使其满足堆定义,然后继续交换堆顶元素与当前末尾元素,反复执行调整+步骤交换,直到整个序列有序。
代码实现:
import java.util.Arrays;
public class HeapSort{
public static void main(String[] args){
int[] arr = {7,6,7,11,5,12,3,0,1};
System.out.println("排序前:" + Arrays.toString(arr));
sort(arr);
System.out.println("排序后:" + Arrays.toString(arr));
}
public static void sort(int[] arr){
//1.构建大顶堆
for(int i = arr.length/2 - 1; i >=0; i--){
//从第一个非叶子节点从下至上,从右至左调整结构
adjustHeap(arr,i,arr.length);
}
//2.调整堆结构+交换堆顶元素与末尾元素
for(int j = arr.length-1; j > 0; j--){
swap(arr,0,j); //交换堆顶元素与末尾元素
adjustHeap(arr,o,j);//重新对堆进行调整
}
}
/*
调整大顶堆(仅是调整过程,建立在大顶堆已构建的基础上)
*/
public static void adjustHeap(int[] arr, int i, int length){
int temp = arr[i];//先取出当前元素i
for(int k = i*2 +1; k < length; k = k*2+1){//从i节点的左子节点开始,也就是2i+1处开始
if(k+1 < length && arr[k] < arr[k+1]){//如果左子节点小于右子节点,k指向右子节点
k++;
}
if(arr[k] > temp){//如果子节点大于父节点,将子节点值赋给父节点(不用进行交换)
arr[i] = arr[k];
i = k;
}else{
break;
}
}
arr[i] = temp;
}
//交换元素
public static void swap(int[] arr,int a, int b){
int temp = arr[a];
arr[a] = arr[b];
arr[b] = temp;
}
}
浙公网安备 33010602011771号