• 【剑指Offer-时间效率】面试题41:数据流中的中位数


    题目描述

    如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。我们使用Insert()方法读取数据流,使用GetMedian()方法获取当前读取数据的中位数。

    思路1

    将数组作为容器,当读取中位数时,先将数组升序排序,然后根据数据中元素的个数是奇数还是偶数返回即可。代码如下:

    class Solution {
    public:
        
        vector<double> v;
        
        void swap(int i, int j)
        {
            double t = v[i];
            v[i] = v[j];
            v[j] = t;
        }
        
        int partition(int left, int right)
        {
            int i = left;
            int j = right;
            double base = v[i];
            while(i<j)
            {
                while(v[j]>=base && i<j)
                    j--;
                while(v[i]<=base && i<j)
                    i++;
                swap(i, j);
            }
            swap(left, i);
            return i;
        }
        
        void quicksort(int left, int right)
        {
            if(left<right)
            {
                int idx = partition(left, right);
                quicksort(left, idx-1);
                quicksort(idx+1, right);
            }
        }
        
        void Insert(int num)
        {
            v.push_back(num);
        }
    
        double GetMedian()
        { 
            quicksort(0, v.size()-1);
            if(v.size()%2!=0)
                return v[v.size()/2];
            else
                return (v[v.size()/2]+v[v.size()/2-1])/2;
        }
    
    };
    

    代码使用了快速排序,时间复杂度为O(nlogn)。

    思路2

    思路2就是书上的方法,用堆来做。容器被中位数分为两部分,左半部分小于中位数,右半部分大于中位数,则可以使用最大堆存储左半部分,使用最小堆存储右半部分,最小堆中的元素大于最大堆中的元素。由于要保持左右元素个数差值不超过1,不妨将第偶数个数(从0开始)插入到最小堆,第奇数个数插入到最大堆,这样中位数或者是最大堆和最小堆堆顶元素和除以2,或者是最小堆的堆顶元素。
    当插入的时候,会出现两种情况:第一、当前元素要插入到最小堆,但是该元素小于最大堆的堆顶元素,如果直接插入最小堆,则不能保证最小堆的元素均大于最大堆中的元素。此时,可以先将该元素插入到最大堆当中,然后将最大堆的堆顶元素插入到最小堆。第二、当前元素要插入到最大堆,但是该元素大于最小堆的堆顶元素。此时,先将该元素插入到最小堆,然后将最小堆的堆顶元素插入到最大堆。使用vector、push_heap和pop_heap来实现堆,代码如下:

    class Solution {
    public:
        
        vector<double> minHeap;
        vector<double> maxHeap;
        
        void Insert(int num)
        {
            if((minHeap.size()+maxHeap.size())%2==0)    //第偶数个元素插入最小堆
            {
                if(maxHeap.size()>0 && num<maxHeap[0])
                {
                    maxHeap.push_back(num);
                    push_heap(maxHeap.begin(), maxHeap.end(), less<double>());
                    num = maxHeap[0];
                    pop_heap(maxHeap.begin(), maxHeap.end(), less<double>());
                    maxHeap.pop_back();
                }
                minHeap.push_back(num);
                push_heap(minHeap.begin(), minHeap.end(), greater<double>());
            }
            else    //第奇数个元素插入最大堆
            {
                if(minHeap.size()>0 && num>minHeap[0])
                {
                    minHeap.push_back(num);
                    push_heap(minHeap.begin(), minHeap.end(), greater<double>());
                    num = minHeap[0];
                    pop_heap(minHeap.begin(), minHeap.end(), greater<double>());
                    minHeap.pop_back();
                }
                maxHeap.push_back(num);
                push_heap(maxHeap.begin(), maxHeap.end(), less<double>());
            }
        }
    
        double GetMedian()
        { 
            int length = minHeap.size()+maxHeap.size();
            if(length%2==0)
                return (minHeap[0]+maxHeap[0])/2;
            else return minHeap[0];
        }
    
    };
    

    向堆中插入一个元素的时间复杂度为O(logn),获取堆顶元素的时间复杂度为O(1),所以获取中位数的时间复杂度为O(1)。

  • 相关阅读:
    Excel操作基本方法 服务器端不用安装Office工具
    使用IrisSkin2给软件"换肤"
    手机进行GPRS连接的代码(C#.NET开发)
    C# DataGridView 倒出word
    win2003优化大全 (转载)
    c# Invoke和BeginInvoke 区别
    关于sqlite3使用top的一些规则总结
    C#拷贝文件和复制文件夹实例代码 C#拷贝文件
    c# FileSystemWatcherc# FileSystemWatcher控件的使用方法控件的使用方法
    所见即所得富文本编辑器实现原理 转
  • 原文地址:https://www.cnblogs.com/flix/p/12507553.html
Copyright © 2020-2023  润新知