《剑指Offer》(41)数流中的中位数及Java的优先队列PriorityQueue

题目:如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。

Java中的优先队列PriorityQueue

java.lang.Object
继承者java.util.AbstractCollection
继承者java.util.AbstractQueue
继承者java.util.PriorityQueue,已实现接口Serializable,Iterable,Collection,Queue

PriorityQueue是Java中的优先队列,它实现了Queue接口,不允许放入null值通过**小顶堆(最小堆)**实现,在JDK1.5中引入。

PriorityQueue对内部的元素采用的是堆排序,而且是最小堆(小顶堆)实现,此队列的头是按指定排序方式确定的最小元素,如果多个元素都是最小值,则头是其中的一个元素。最小堆排序只能保证堆顶元素是最小的,整个堆并不是有序的。

该集合内可以存放基本数据类型对象包装类(如:Integer,Long等)或者自定义的类。对于基本数据类型对象包装类,优先队列中的元素默认排列顺序是降序排列(小顶堆/最小堆),对应PriorityQueue的空参数构造函数,根据元素的自然顺序来排序(此时不允许插入不可比较的对象,否则会发生ClassCastException);如果想要升序排列(大顶堆/最大堆),可以自定义一个比较器(定义一个类或者匿名内部类实现Comparator< T >接口,并复写该接口中的public int compare(T o1,T o2)方法),并将该比较器作为参数传递给PriorityQueue类的构造函数。但是对于自定义的类而言,需要自己定义比较器(定义一个类或者匿名内部类实现Comparator< T >接口,并复写该接口中的public int compare(T o1,T o2)方法)。

//实现大顶堆,自定义比较器
//创建一个匿名内部类对象
Comparator<Integer> comp = new Comparator<Integer>()
{
	public int compare(Integer e1,Integer e2)
	{
		return e2 - e1;
	}
};

优先队列PriorityQueue是无界的,它会根据队列中元素的数量动态地增长。但是它有一个内部容量,控制着用于存储队列元素的数组的大小。它总是至少与队列的大小相同。随着不断向优先级队列中添加元素,其容量会自动增加,因而无需指定容量增加策略的细节。

int i = size;
if(i >= queue.size)
	grow(i + 1);
size = i + 1;	

该类中的构造方法如下:
在这里插入图片描述
该类中的常用方法如下:在这里插入图片描述

PS:
(1)优先级队列是通过数组实现的,但是该数组大小可以动态增加,容量无限。

(2)PriorityQueue不是同步的,不是线程安全的。对应的PriorityBlockingQueue 类是线程安全的类。

(3)队列中不能存放 null 元素。

(4)此实现为插入方法(offer(E e)、add(E e) 、poll()、remove()方法)提供 O(log(n)) 时间;为 remove(Object o) 和contains(Object o) 方法提供线性时间;为检索方法(peek()、element() 和 size())提供固定时间。

(5)方法iterator()中提供的迭代器并不保证以有序的方式遍历优先级队列中的元素。如果需要按顺序遍历,可以使用 Arrays.sort(arr)的方法(默认升序)。

转载:toArray()方法的使用问题https://blog.csdn.net/zy103118/article/details/81479118

Object[] arr = queue.toArray();//将集合转换为数组
Arrays.sort(arr);//默认是升序

(6)可以在PriorityQueue的构造函数中指定如何排序。

空参数构造函数PriorityQueue():使用默认的初始容量(大小为11)创建一个 PriorityQueue,并根据其自然顺序来排序其元素(元素自身必须具备比较性即实现了 Comparable接口,否则会发生ClassCastException)。

PriorityQueue(int initialCapacity):使用指定的初始容量创建一个 PriorityQueue,并根据其自然顺序来排序其元素(元素自身必须具备比较性即实现了 Comparable接口)。

PriorityQueue(int initialCapacity, Comparator comparator):使用指定的初始容量创建一个 PriorityQueue,并根据指定的比较器comparator来排序其元素(有了comparator,优先按照comparator进行排序)。

(7)此类及其迭代器实现了 Collection 接口和 Iterator 接口的所有可选方法。

数据流中的中位数的Java代码如下:

import java.util.*;
public class Solution 
{
   //优先队列,是以最小堆(小顶堆)实现的,堆顶元素必为最小值,优先队列头部的元素是队列中的最小元素
   private PriorityQueue<Integer> min = new PriorityQueue<Integer>();

   //自定义比较器并将其作为参数传入PriorityQueue的构造函数中,实现大顶堆
   private Comparator<Integer> comp = new Comparator<Integer>()//建立一个匿名内部类对象
    {
       public int compare(Integer e1,Integer e2)
      {
           return e2 - e1;
       }   
   };
   private PriorityQueue<Integer> max = new PriorityQueue<Integer>(comp);
 
   public void Insert(Integer num) 
    {
        int value = 0;
       //为保证数据平均分配到两个堆中,因此两个堆中的数据的数目之差不能超过1(<=1)
       //数据总数目是偶数时,将新数据插入最小堆
       if(((min.size() + max.size()) & 1) == 0)//位运算比乘除运算的效率要高
       {
           //为了保证大顶堆中的元素都小于小顶堆中的元素
           //判断该新数据是否小于大顶堆中的元素
           if(max.size() > 0 && num < max.peek())//获取队列头部的元素(在大顶堆中,是此优先队列中的最大元素)
           {
                //先把新数据插入大顶堆,然后把大顶堆中的最大元素取出插入小顶堆
                max.add(num);
                value = max.poll();
                min.add(value);
           }
           else
                min.add(num);
       }
       else
       //数据总数目是奇数时,将新数据插入最大堆
       {
           //为了保证大顶堆中的元素都小于小顶堆中的元素
           //判断该新数据是否大于小顶堆中的元素
           if(min.size() > 0 && num > min.peek())//获取队列头部的元素(在小顶堆中,是此优先队列中的最小元素)
           {
                //先把新数据插入小顶堆,然后把小顶堆中的最小元素取出插入大顶堆
                min.add(num);
                value = min.poll();
                max.add(value);
          }
           else
                max.add(num);
       }
    }

   public Double GetMedian() 
    {
       int size = min.size() + max.size();
       double median = 0.0;
       if((size & 1) == 1)//数据总数目为奇数
           median = min.peek();
       else//数据总数目为偶数
           median = (min.peek() + max.peek()) / 2.0;

       return median;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值