优先级队列PriorityQueue,这名字听起来吊炸天,其实就是一个堆。
PriorityQueue 是一个基于优先级堆的无界队列, 它的元素是按照自然顺序(natural order)排序的。 在创建的时候, 我们可以给它提供一个负责给元素排序的比较器。 PriorityQueue 不允许 null 值, 因为他们没有自然顺序, 或者说他们没有任何的相关联的比较器。 最后, PriorityQueue 不是线程安全的, 入队和出队的时间复杂度是 O(log(n))。
堆树的定义如下:
(1) 堆树是一颗完全二叉树;
(2) 堆树中某个节点的值总是不大于或不小于其孩子节点的值;
(3) 堆树中每个节点的子树都是堆树。
Java中PriorityQueue通过二叉小顶堆实现,可以用一棵完全二叉树表示。
leftNo = parentNo2+1
rightNo = parentNo2+2
parentNo = (nodeNo-1)/2
PriorityQueue的peek()和element操作是常数时间,add(), offer(), 无参数的remove()以及poll()方法的时间复杂度都是log(N)。
有关堆排序的知识,那么给一个数组,如何进行堆排序呢,同时这也是面试中会考察的:
package com.fwc;
public class heapsort {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
//定义整型数组
int[] arr = {1,5,6,8,7,2,3,4,9};
//调用堆排序数组
HeapSort(arr);
//输出排序后的数组
for(int i=0;i<arr.length;i++)
{
System.out.print(arr[i]+" ");
}
}
//堆排序函数
public static void HeapSort(int[] arr)
{
int n = arr.length-1;
for(int i=(n-1)/2;i>=0;i--)
{
//构造大顶堆,从下往上构造
//i为最后一个根节点,n为数组最后一个元素的下标
HeapAdjust(arr,i,n);
}
for(int i=n;i>0;i--)
{
//把最大的数,也就是顶放到最后
//i每次减一,因为要放的位置每次都不是固定的
swap(arr,i);
//再构造大顶堆
HeapAdjust(arr,0,i-1);
}
}
//构造大顶堆函数,parent为父节点,length为数组最后一个元素的下标
public static void HeapAdjust(int[] arr,int parent,int length)
{
//定义临时变量存储父节点中的数据,防止被覆盖
int temp = arr[parent];
//2*parent+1是其左孩子节点
for(int i=parent*2+1;i<=length;i=i*2+1)
{
//如果左孩子大于右孩子,就让i指向右孩子
if(i<length && arr[i]<arr[i+1])
{
i++;
}
//如果父节点大于或者等于较大的孩子,那就退出循环
if(temp>=arr[i])
{
break;
}
//如果父节点小于孩子节点,那就把孩子节点放到父节点上
arr[parent] = arr[i];
//把孩子节点的下标赋值给parent
//让其继续循环以保证大根堆构造正确
parent = i;
}
//将刚刚的父节点中的数据赋值给新位置
arr[parent] = temp;
}
//定义swap函数
//功能:将跟元素与最后位置的元素交换
//注意这里的最后是相对最后,是在变化的
public static void swap(int[] arr,int i)
{
int temp = arr[0];
arr[0] = arr[i];
arr[i] = temp;
}
}
add(E e)和offer(E e)的语义相同,都是向优先队列中插入元素,只是Queue接口规定二者对插入失败时的处理不同,前者在插入失败时抛出异常,后则则会返回false。对于PriorityQueue这两个方法其实没什么差别
element()和peek()的语义完全相同,都是获取但不删除队首元素,也就是队列中权值最小的那个元素,二者唯一的区别是当方法失败时前者抛出异常,后者返回null。根据小顶堆的性质,堆顶那个元素就是全局最小的那个;由于堆用数组表示,根据下标关系,0下标处的那个元素既是堆顶元素。所以直接返回数组0下标处的那个元素即可
remove()和poll()方法的语义也完全相同,都是获取并删除队首元素,区别是当方法失败时前者抛出异常,后者返回null。由于删除操作会改变队列的结构,为维护小顶堆的性质,需要进行必要的调整
remove(Object o)方法用于删除队列中跟o相等的某一个元素(如果有多个相等,只删除一个),该方法不是Queue接口内的方法,而是Collection接口的方法。由于删除操作会改变队列结构,所以要进行调整;又由于删除元素的位置可能是任意的,所以调整过程比其它函数稍加繁琐。具体来说,remove(Object o)可以分为2种情况:
- 删除的是最后一个元素。直接删除即可,不需要调整。
- 删除的不是最后一个元素,从删除点开始以最后一个元素为参照调用一次siftDown()即可。此处不再赘述2. 删除的不是最后一个元素,从删除点开始以最后一个元素为参照调用一次siftDown()即可。此处不再赘述