题意整理
- 需要一个数据结构来存储从数据流吐出的整数。
- 每收到一个整数,都可以得到当前数据结构中所有整数的中位数。
方法一(大顶堆与小顶堆)
1.解题思路
- 初始化一个大顶堆和一个小顶堆。
- 当两个堆大小相等时,将当前元素先入大顶堆,再弹出大顶堆的堆顶元素,将弹出的堆顶元素入小顶堆;如果两个堆大小不相等,则先将当前元素入小顶堆,再弹出堆顶元素入大顶堆。
- 当两个堆大小相等时,数据流总共有偶数个,所以中位数为两个堆堆顶元素之和的一半;当大小不相等时,总共有奇数个,中位数恰好是小顶堆堆顶元素值。
动图展示:
2.代码实现
import java.util.*;
public class Solution {
/**
* the medians
* @param operations int整型二维数组 ops
* @return double浮点型一维数组
*/
public double[] flowmedian (int[][] operations) {
//初始化结果数组
int len=0;
for(int[] opera:operations){
if(opera[0]==2){
len++;
}
}
double[] res=new double[len];
//初始化MedianHolder类
MedianHolder median=new MedianHolder();
int id=0;
for(int[] opera:operations){
//如果opt=1,则加入到结构中
if(opera[0]==1){
median.addNum(opera[1]);
}
//如果opt=2,则取出当前中位数
else{
res[id++]=median.findMedian();
}
}
return res;
}
class MedianHolder{
PriorityQueue<Integer> min;
PriorityQueue<Integer> max;
MedianHolder() {
min=new PriorityQueue<>();
max=new PriorityQueue<>((o1,o2)->o2-o1);
}
//添加当前数字
public void addNum(int num) {
if(min.size()==max.size()){
max.offer(num);
min.offer(max.poll());
}
else{
min.offer(num);
max.offer(min.poll());
}
}
//查找当前中位数
public double findMedian() {
//如果数据流中没有数字,则返回-1
if(min.size()==0&&max.size()==0) return -1.0;
if(min.size()==max.size()){
return (min.peek()+max.peek())/2.0;
}
else{
return min.peek();
}
}
}
}3.复杂度分析
- 时间复杂度:堆的插入和弹出操作的时间复杂度都是
,获取堆顶元素不需要重新调整堆,可以在
的时间内找到堆顶元素。所以添加的时间复杂度是
,查找的时间复杂度是
。
- 空间复杂度:假设数据流的大小为n,最坏情况下,大顶堆或小顶堆的大小为n,所以空间复杂度为
。
方法二(维护递增数据流+二分查找)
1.解题思路
核心代码的思路是用list维护一个单调递增的数据流,当有数据进来时,通过二分查找找到这个数在list中的位置,然后插入到这个对应的位置。当list的容量是奇数时,中间的那个数即是数据流的中位数;当list的容量是偶数时,中间两个数的平均值即是数据流的中位数。
2.代码实现
import java.util.*;
public class Solution {
/**
* the medians
* @param operations int整型二维数组 ops
* @return double浮点型一维数组
*/
public double[] flowmedian (int[][] operations) {
//初始化结果数组
int len=0;
for(int[] opera:operations){
if(opera[0]==2){
len++;
}
}
double[] res=new double[len];
//初始化MedianHolder类
MedianHolder median=new MedianHolder();
int id=0;
for(int[] opera:operations){
//如果opt=1,则加入到结构中
if(opera[0]==1){
median.addNum(opera[1]);
}
//如果opt=2,则取出当前中位数
else{
res[id++]=median.findMedian();
}
}
return res;
}
class MedianHolder{
//维护一个单调递增的数据流
List<Integer> list;
MedianHolder() {
list=new ArrayList<>();
}
public void addNum(int num) {
//如果数据流为空或者num大于数据流最大值,则直接添加
if(list.size()==0||list.get(list.size()-1)<=num){
list.add(num);
return;
}
//找到num在数据流中的位置,并添加
int index=binarySearch(list,num);
list.add(index,num);
}
public double findMedian() {
int n=list.size();
//如果数据流中没有数字,则返回-1
if(n==0) return -1.0;
if(n%2==0){
return (list.get(n/2)+list.get(n/2-1))/2.0;
}
else{
return (double)list.get(n/2);
}
}
//二分查找
private int binarySearch(List<Integer> list,int target){
int l=0,r=list.size()-1;
while(l<r){
int mid=l+(r-l)/2;
if(list.get(mid)>=target){
r=mid;
}
else{
l=mid+1;
}
}
return l;
}
}
}3.复杂度分析
- 时间复杂度:添加数据时,使用了二分查找来确定数据的位置,时间复杂度是
,然后将数据添加到查找的位置,最坏情况下,需要移动n个元素,所以添加的时间复杂度是
,查找中位数时,直接根据索引获取数据,所以查找的时间复杂度是
。
- 空间复杂度:假设数据流的大小为n,list需要存储总共n个数据,所以空间复杂度为
。

京公网安备 11010502036488号