数据流中的中位数

博客围绕数据流中位数问题展开,介绍了设计支持添加整数和返回中位数操作的数据结构。给出样例展示输入输出情况,重点分析了六种解法,包括用未排序数组、排序数组、排序链表、二叉搜索树、平衡二叉树和双堆等,还提及各方法的时间复杂度。

题目描述:

中位数是有序列表中间的数。如果列表长度是偶数,中位数则是中间两个数的平均值。
例如,[2,3,4] 的中位数是 3
[2,3] 的中位数是 (2 + 3) / 2 = 2.5
设计一个支持以下两种操作的数据结构:

  • void addNum(int num) - 从数据流中添加一个整数到数据结构中。
  • double findMedian() - 返回目前所有元素的中位数。

样例:

输入:1, 2, 3, 4
输出:1,1.5,2,2.5
解释:每当数据流读入一个数据,就进行一次判断并输出当前的中位数。

分析:

  • 第一种方法:利用未排序的数组存储添加的数据,即每次添加不需要使数组内的元素有序,则插入的时间复杂度为O(1),每次寻找中位数的时间复杂度为O(n),利用快速排序的思想寻找数组中第n/2大的元素,每次将小于该元素的数据放在左侧,大于该元素的数据放在右侧。
  • 第二种方法:利用排序的数组存储数据,即每次添加的数据要放在数组中合适的位置,使得数组的元素按序排列,因此添加数据的时间复杂度是O(n),而查找中位数的时间复杂度是O(1)。
  • 第三种方法:利用排序的链表存储数据,添加数据到链表中合适的位置其时间复杂度是O(n)。定义两个指针指向链表中的中间节点(如果链表中节点数目是奇数,则这两个指针指向同一个节点),那么可以在O(1)的时间内得出中位数。
  • 第四种方法是建立二叉搜索树可以使插入的时间复杂度变为O(logn),但是当二叉搜索树极度不平衡时时间复杂度仍为O(n)。为了得到中位数,可以在二叉树的表示中添加成员变量size表示二叉树中节点的数目,那么查找中位数的时间复杂度为O(logn),最差情况仍需要O(n)。
  • 第五种方法是建立平衡的二叉树:插入元素的时间复杂度为O(logn),获取中位数的时间复杂度为O(1)。因为大多数库函数都没有实现该数据结构,所有考虑用堆处理即第六种方法。
  • 第六种方法是基于第五种方法的思想维护两个堆即最大堆和最小堆,使得最小堆的堆顶元素大于最大堆的堆顶元素,并确保两个堆内的元素数目不能相差大于1,向堆内添加元素的时间复杂度是O(logn),取出中位数的时间复杂度是O(1)。
//执行用时 : 257 ms, 在Find Median from Data Stream的Java提交中击败了70.92% 的用户
	//内存消耗 : 67.7 MB, 在Find Median from Data Stream的Java提交中击败了67.73% 的用户
	PriorityQueue<Integer> maxheap;
	PriorityQueue<Integer> minheap;
    /** initialize your data structure here. */
    public MedianFinder() {
        maxheap=new PriorityQueue<>((a1, a2) -> a2 - a1);
        minheap=new PriorityQueue<>();
    }
    
    public void addNum(int num) {
        if(maxheap.isEmpty()) {
        	maxheap.offer(num);
        	return;
        }
        if(num>maxheap.peek()) {
        	minheap.offer(num);
        }else {
        	maxheap.offer(num);
        }
        if(maxheap.size()-minheap.size()>1)
        	minheap.offer(maxheap.poll());
        else if(minheap.size()-maxheap.size()>1)
        	maxheap.offer(minheap.poll());
        
      
    }
    
    public double findMedian() {
        double num=0;
        int maxlen=maxheap.size();
        int minlen=minheap.size();
        if(maxlen==minlen) {
        	if(maxlen==0)
        		return num;
        	num=(maxheap.peek()+minheap.peek())/2.0;
        	return num;
        }
        if(maxlen>minlen)
        	return maxheap.peek();
        if(minlen>maxlen)
        	return minheap.peek();
        return num;
    }
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值