zoukankan      html  css  js  c++  java
  • [LeetCode] 295. Find Median from Data Stream(在数据流中寻找中位数)

    Description

    Median is the middle value in an ordered integer list. If the size of the list is even, there is no middle value. So the median is the mean of the two middle value.
    中位数是有序列表的中间值。如果列表的长度是偶数,则没有中间值。此时中位数是中间两个数的平均值。

    For example,
    例如,

    [2,3,4], the median is 3
    [2, 3, 4] 的中位数是 3

    [2,3], the median is (2 + 3) / 2 = 2.5
    [2, 3] 的中位数是 (2 + 3) / 2 = 2.5

    Design a data structure that supports the following two operations:
    设计一个数据结构,使其能支持以下操作:

    • void addNum(int num) - Add a integer number from the data stream to the data structure.
      void addNum(int num) - 往此数据解构里添加一个数。
    • double findMedian() - Return the median of all elements so far.
      double findMedian() - 返回此数据结构中目前的中位数。

    Example

    addNum(1)
    addNum(2)
    findMedian() -> 1.5
    addNum(3) 
    findMedian() -> 2
    

    Follow up

    1. If all integer numbers from the stream are between 0 and 100, how would you optimize it?
      如果流里的数据都在 0 到 100 之间,你该如何优化?
    2. If 99% of all integer numbers from the stream are between 0 and 100, how would you optimize it?
      如果流里 99% 的数据在 0 到 100 之间,你该如何优化?

    Solution

    看到题目标签里有个 Heap,想了想,这题的解法应该就是维护堆了。起初的思路是:维护一个大顶堆和一个小顶堆。插入的时候同时往这两个地方插,并维护堆的大小在元素总数的一半。然后代码 WA 了。

    class MedianFinder() {
        /** initialize your data structure here. */
        private val minHeap = PriorityQueue<Int>()
        private val maxHeap = PriorityQueue<Int>(compareByDescending { it })
        private var size = 0
    
    
        fun addNum(num: Int) {
            size++
            minHeap.offer(num)
            maxHeap.offer(num)
            trimHeap()
        }
    
        fun findMedian(): Double {
            return if (size % 2 == 0) {
                (minHeap.peek() + maxHeap.peek()) / 2.0
            } else {
                maxHeap.peek().toDouble()
            }
        }
    
        private fun trimHeap() {
            val targetSize = if (size % 2 == 0) {
                size / 2
            } else {
                size / 2 + 1
            }
    
            while (minHeap.size != targetSize) {
                minHeap.poll()
            }
            while (maxHeap.size != targetSize) {
                maxHeap.poll()
            }
        }
    }
    

    问题出在哪里?翻阅 discussion 后懂了:用两个堆没错,但我维护的方式错了。对于一组数,我们需要做的事将其分为前后两部份(排序后的),中位数就从前半段的最后一个与后半段的第一个中决定。根据堆的性质,不难想到:前半段用大顶堆,后半段用小顶堆。插入元素时,先入大顶堆,再调整两堆,保持大顶堆比小顶堆元素数量相等或多一。代码如下:

    class MedianFinder() {
        /** initialize your data structure here. */
        private val small = PriorityQueue<Int>(compareByDescending { it })
        private val large = PriorityQueue<Int>()
    
    
        fun addNum(num: Int) {
            large.offer(num)
            small.offer(large.poll())
            if (large.size < small.size) {
                large.offer(small.poll())
            }
        }
    
        fun findMedian(): Double {
            return if (large.size > small.size) {
                large.peek().toDouble()
            } else {
                (large.peek() + small.peek()) / 2.0
            }
        }
    }
    
  • 相关阅读:
    记一次CTF比赛过程与解题思路MISC部分
    使用requests爬虫遇到的一个奇葩的问题:UnicodeEncodeError: 'latin1' codec can't encode character
    纯前端实现词云展示+附微博热搜词云Demo代码
    亚马逊精细化选品服务
    乔布斯访谈笔记
    使用腾讯云轻量级服务器
    centos 设置阿里的yum源
    云未来、新可能 绿色、无处不在、可信的计算
    OpenKruise v1.0:云原生应用自动化达到新的高峰
    服务发现与配置管理高可用最佳实践
  • 原文地址:https://www.cnblogs.com/zhongju/p/14163340.html
Copyright © 2011-2022 走看看