面试题64:数据流中的中位数

来源:互联网 发布:免费机械制图软件 编辑:程序博客网 时间:2024/04/27 10:09

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


可以将从数据流中读到的数据放入数据容器

1.未排序的数组,可以使用partition函数找到数组的中位数,插入数字和找出中位数的时间复杂度分别是O(1)和O(n)

2.在插入数据的时候对数组排序,插入时间复杂度为O(n),查找中位数O(1)

3.排序的链表,插入数据时间复杂度O(N),如果定义两个指针指向链表中间节点,时间复杂度时O(1)

4.二叉搜索树,插入数据O(logn),当二叉搜索树极度不平衡看起来像排序链表的时候,插入数据时间复杂度仍是O(n),为了得到中位数,可以在结点中添加一个表示子树节点数目的字段,然后可以在O(logn)得到中位数,最差情况仍然需要O(n).

5.AVL树,插入结点O(logn),获取中位数O(1)。

6.最大堆最小堆,如果将数据流中的数字排序,中位数可以由P1 P2指向 的数得到,如果数目是奇数,P1=P2,可以发现排序的时候,P1指向的数据是左边部分最大的数,P2指向的是数据右边最小的数,且左边的数小于右边的数,所以可以将左边的数放入一个大顶堆中,右边的数放入小顶堆中,堆顶就是对应的P1和P2。

插入时间复杂度为O(logn),获取中位数O(1)


步骤:

1.为了使数据平均分配,两个堆中的数目之差不能超过1,所以当数据总数目是偶数时把新的数据插入到最小堆,否则插入到最大堆

2.为了保证最大堆中的所有数据小于最小堆,当数据数目是偶数时,需要插入到小顶堆,但是此时数据比大顶堆中的堆顶的值要小,那么就将数据插入到大顶堆中,因为大顶堆根节点是最大元素,然后将根节点元素插入到小顶堆中。

3.当把数据插入到最大堆中但这个数据大于最小堆的堆顶的值时,与上面情况处理办法一样。



如1 3 7 9 5

(1)先将1插入大顶堆,插入3的时候总数目是2,3大于大顶堆的1,插入到小顶堆,此时

         1                                    3

    大顶堆                          小顶堆

(2)7的时候总数目为奇数,但是7大于小顶堆的3,插入小顶堆,然后将3插入大顶堆

         3                                   7

     1                                       

    大顶堆                          小顶堆

(3)9插入到小顶堆

         3                                   7

     1                                          9

    大顶堆                          小顶堆

(4)5<7直接插入到大顶堆

         5                                  7

     1                                            9

         3                                         

    大顶堆                          小顶堆

所以中位数为小顶堆的堆顶7


template<typename T>class DynamicArray{public:void Insert(T num){if(((min.size()+max.size())&1)==0)//数据总数为偶数时,在最小堆中插入元素{if(max.size()>0&&num<max[0])//如果最大堆结点个数不为0并且当前数据的值小于最大堆的根节点的值{max.push_back(num);//将数据插入最大堆,插入到末尾push_heap(max.begin(),max.end(),less<T>());//重新调整堆序num=max[0];//获取最大堆根节点的值pop_heap(max.begin(),max.end(),less<T>());//取出堆顶元素,放到vector末尾max.pop_back();//将末尾元素删除}min.push_back(num);//将数据插入最小堆push_heap(min.begin(),min.end(),greater<T>());}else//数据总数为奇数时,在最大堆中插入元素{if(min.size()>0&&num>min[0]){min.push_back(num);push_heap(min.begin(),min.end(),greater<T>());num=min[0];pop_heap(min.begin(),min.end(),greater<T>());min.pop_back();}max.push_back(num);push_heap(max.begin(),max.end(),less<T>());}}//获取中位数T GetMedian(){int size=min.size()+max.size();if(size==0)throw exception("No numbers are available");T median=0;//如果数据总数是奇数if((size&1)==1){median=min[0];//返回小顶堆的堆顶元素}else{median=(max[0]+min[0])/2;//返回两个堆顶的值的和再除以2}return median;}//使用vector模拟大顶堆和小顶堆private:vector<T> min;vector<T> max;};



------来自剑指offer


java:

public class MedianNumber {static TreeSet<Integer> tsMin=new TreeSet<Integer>();//最小堆static TreeSet<Integer> tsMax=new TreeSet<Integer>();//最大堆/** * 题目:如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。 * 如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。 *  */public static void main(String[] args) {insert(1);insert(3);insert(5);insert(7);insert(9);int median=getMedian();System.out.println(tsMin);System.out.println(tsMax);System.out.println(median);}//Treeset模拟堆,tresset会将插入的元素按大小排列//treeSet中first()方法返回第一个值也就是最小值,last()方法返回最大值//最大堆中放置的元素是中位数左边的数字//最小堆中放置的元素是中位数右边的数字public static void insert(int number){if((tsMin.size()+tsMax.size())%2==0)//数据总数为偶数,将数插入到最小堆{if(tsMax.size()!=0&&number<tsMax.last())//如果插入的元素比最大堆中最大元素还小{//将当前元素先插入到最大堆tsMax.add(number);number=tsMax.last();//获取最大堆中最大的元素tsMax.remove(number);}tsMin.add(number);}else//插入到最大堆{if(tsMin.size()!=0&&number>tsMin.first())//如果插入元素比最小堆的最小元素还大{//将当前元素先插入到最大堆tsMin.add(number);number=tsMin.first();//获取最小堆中最小的元素tsMin.remove(number);}tsMax.add(number);}}public static int getMedian(){int size=tsMin.size()+tsMax.size();if(size%2!=0)return tsMax.last();elsereturn (tsMax.last()+tsMin.first())/2;}}


0 0
原创粉丝点击