priorityqueue排序,priorityqueue排序

priorityqueue排序,priorityqueue排序,解析Java中PriorityQueue优先级队列结构的源码及用法

PriorityQueue是队列结构的一种,是0个或0个以上元素的集合,每个元素都有优先级。优先级队列内置于JDK中。本文分析了Java中优先级队列结构的源代码和用法。

一、PriorityQueue的数据结构

JDK7中PriorityQueue的数据结构是二进制堆。准确地说,是最小堆。

二叉堆是一种特殊的堆,类似于完全二叉树。二叉堆满足特征:父节点的键值始终与任意子节点的键值保持固定的顺序关系,每个节点的左子树和右子树都是一个二叉堆。

当父节点的键值总是大于或等于任何子节点的键值时,就是最大堆。当父节点的键值总是小于或等于任何子节点的键值时,就是最小堆。

下图是最大的堆。

priorityQueue的队列头是给定顺序中最小的元素。

PriorityQueue不允许空值,并且不支持不可比较的对象。PriorityQueue要求使用Compare和Comparator接口对对象进行排序,排序时会根据优先级处理其中的元素。

priorityQueue的大小没有限制,但是初始大小可以在创建时指定。当添加队列元素时,队列将自动扩展。

PriorityQueue不是线程安全的,类似的PriorityBlockingQueue是线程安全的。

我们知道队列遵循先进先出的模式,但是有时需要根据优先级处理队列中的对象。例如,假设我们有一个应用程序,它在每天的交易时间内生成股票报告,需要处理大量的数据,需要大量的处理时间。当客户向这个应用程序发送请求时,它实际上进入了队列。我们需要首先处理优先客户,然后是普通用户。在这种情况下,Java的PriorityQueue会很有帮助。

PriorityQueue是一个基于优先级堆的无限队列。默认情况下,这个优先级队列中的元素可以自然排序,或者在实例化队列时由提供的比较器排序。

优先级队列不允许空值,也不支持不可比的对象,比如用户定义的类。优先级队列要求使用Java Comparable和Comparator接口对对象进行排序,排序时会根据优先级处理其中的元素。

优先级队列的头是基于自然排序或比较排序的最小元素。如果有多个相同排序的对象,可以随机选择其中的任何一个。当我们得到队列时,我们返回队列的头对象。

优先级队列的大小没有限制,但是初始大小可以在创建时指定。当我们向优先级队列添加元素时,队列大小将自动增加。

PriorityQueue是非线程安全的,所以Java为Java多线程环境提供了PriorityBlockingQueue(实现BlockingQueue接口)。

二、PriorityQueue源码分析

成员:

priavte临时对象[]队列;

private int size=0;

1.PriorityQueue构造小顶堆的过程

这里我们取priorityQueue构造函数传入的一个容器作为参数PriorityQueue (college?延伸E的示例:

构建小顶桩的过程大致分为两个步骤:

复制容器数据并检查容器数据是否为空。

私有void initElementsFromCollection(集合?扩展E c) {

object[]a=c . to array();

//如果c.toArray错误地没有返回Object[],就复制它。

if (a.getClass()!=对象[]。类)

a=Arrays.copyOf(a,a.length,Object[]。类);

int len=a.length

if (len==1 || this.comparator!=空)

for(int I=0;我len我)

if (a[i]==null)

抛出新的NullPointerException();

this . queue=a;

this . size=a . length;

}

调整数据以满足小顶桩的结构。

首先介绍了siftUp和siftDown两种调整方法。

SiftDown:当给定一个初始化元素时,应该调整该元素以满足最小堆的结构属性。因此,从上到下不断地比较和交换元素X与子节点的键值,直到你发现元素X的键值小于或等于子节点的键值(也就是确保它小于它的左右节点),或者下降到叶子节点。

例如如下的示意图,调整9这个节点:

私有void向下筛选comparable(int k,E x) {

可比?超E键=(可比?超e)x;

int half=size 1;//size/2是第一个叶子结点的下标

//只要没到叶子节点

while (k half) {

int child=(k 1)1;//左孩子

object c=queue[child];

int right=child 1;

//找出左右孩子中小的那个

如果(大小合适

((可比?超级E) c).compareTo((E) queue[right]) 0)

c=队列[子=右];

if (key.compareTo((E) c)=0)

打破;

queue[k]=c;

k=孩子;

}

queue[k]=key;

}

siftUp:优先级队列每次新增加一个元素的时候是将新元素插入对尾的。因此,应该与siftDown有同样的调整过程,只不过是从下(叶子)往上调整。

例如如下的示意图,填加键为3的节点:

私有空siftup comparable(int k,E x) {

可比?超E键=(可比?超e)x;

while (k 0) {

int parent=(k-1)1;//获取父母下标

object e=queue[parent];

if (key.compareTo((E) e)=0)

打破;

queue[k]=e;

k=父母;

}

queue[k]=key;

}

总体的建立小顶堆的过程就是:

私有void initFromCollection(集合?扩展中文){

initElementsFromCollection(c);

heap ify();

}

其中使肥胖就是siftDown的过程。

2.PriorityQueue容量扩容过程

从实例成员可以看出,优先级队列维护了一个对象[],因此它的扩容方式跟顺序表数组列表相差不多。

这里只给出生长方法的源码

私有void grow(int minCapacity) {

int oldCapacity=queue.length

//小的话双倍大小;其他增长50%

int新容量=旧容量((旧容量64)?

(旧容量2):

(旧容量1));

//溢出感知代码

if (newCapacity - MAX_ARRAY_SIZE 0)

新增产能=巨大产能(最小产能);

queue=Arrays.copyOf(queue,new capacity);

}

可以看出,当数组的容量不大的时候,每次扩容也不大。当数组容量大于64的时候,每次扩容双倍。

三、PriorityQueue的应用

eg1:

这里给出一个最简单的应用:从动态数据中求第K个大的数。

思路就是维持一个大小=k的小顶堆。

//数据是动态数据

//堆维持动态数据的堆

//分辨率用来保存第K大的值

public boolean kthLargest(int data,int k,PriorityQueueInteger heap,int[] res) {

if(heap.size() k) {

heap.offer(数据);

if(heap.size()==k) {

RES[0]=堆。peek();

返回真实的

}

返回错误的

}

if(heap.peek() data) {

堆。poll();

heap.offer(数据);

}

RES[0]=堆。peek();

返回真实的

}

eg2:

我们有一个用户类顾客,它没有提供任何类型的排序。当我们用它建立优先队列时,应该为其提供一个比较器对象。

Customer.java

包com。日志开发。收藏;

公共类客户{

私有int id

私有字符串名称;

公共客户(int i,String n){

这个。id=I;

这个。name=n;

}

public int getId() {

返回id;

}

公共字符串getName() {

返回名称;

}

}

我们使用Java 语言(一种计算机语言,尤用于创建网站)语言(一种计算机语言,尤用于创建网站)随机数生成随机用户对象。对于自然排序,我们使用整数对象,这也是一个封装过的Java 语言(一种计算机语言,尤用于创建网站)语言(一种计算机语言,尤用于创建网站)对象。

下面是最终的测试代码,展示如何使用优先级队列:

PriorityQueueExample.java

包com。日志开发。收藏;

导入Java。util。比较器;

导入Java。util。优先级队列;

导入Java。util。排队;

导入Java。util。随机;

public class PriorityQueueExample {

公共静态void main(String[] args) {

//优先队列自然排序示例

queue integerPriorityQueue=新优先级队列(7);

Random rand=new Random();

for(int I=0;i7;i ){

integerPriorityQueue.add(新整数(兰德。nextint(100)));

}

for(int I=0;i7;i ){

整数in=integerpriorityqueue。poll();

System.out.println(处理整数: in );

}

//优先队列使用示例

queue customer customerporityqueue=新优先级队列(7,id比较器);

addDataToQueue(customerPriorityQueue);

pollDataFromQueue(customerPriorityQueue);

}

//匿名比较仪实现

公共静态比较器客户id比较器=新比较器客户(){

@覆盖

公共(同Internationalorganizations)国际组织比较(客户c1,客户c2) {

return(int)(C1。getid()-C2。getid());

}

};

//用于往队列增加数据的通用方法

私有静态void addDataToQueue(队列客户客户优先级队列){

Random rand=new Random();

for(int I=0;i7;i ){

int id=rand。nextint(100);

customerPriorityQueue.add(新客户(id, Pankaj id));

}

}

//用于从队列取数据的通用方法

私有静态void pollDataFromQueue(队列客户客户优先级队列){

while(true){

customer cust=customerporityqueue。poll();

if(cust==null)break;

System.out.println(处理ID= cust.getId()的客户);

}

}

}

注意我用实现了比较仪接口的Java 语言(一种计算机语言,尤用于创建网站)语言(一种计算机语言,尤用于创建网站)匿名类,并且实现了基于身份证明(识别)的比较器。

当我运行以上测试程序时,我得到以下输出:

处理整数:9

处理整数:16

处理整数:18

处理整数:25

处理整数:33

处理整数:75

处理整数:77

正在处理ID=6的客户

正在处理ID=20的客户

正在处理ID=24的客户

正在处理ID=28的客户

正在处理ID=29的客户

处理ID=82的客户

处理ID=96的客户

从输出结果可以清楚的看到,最小的元素在队列的头部因而最先被取出。如果不实现比较器,在建立客户优先级队列时会抛出ClassCastException。

线程"主要"中出现异常Java。郎。classcastexception:com。日志开发。收藏。顾客不能转换为可比较的

位于Java。util。优先级队列。siftupcumbrable(优先级队列。Java:633)

位于Java。util。优先级队列。向上筛选(优先级队列。Java:629)

位于Java。util。优先级队列。报价(优先排队。Java:329)

位于Java。util。优先级队列。添加(优先级队列。Java:306)

位于com。日志开发。收藏。priorityqueueexample。adddatatoqueue(priorityqueueexample。Java:45)

位于com。日志开发。收藏。priorityqueueexample。main(priorityqueueexample。Java:25)

priorityqueue排序,priorityqueue排序