数组流中的中位数

如何找到数组流中的中位数

题目

  • 数据流中的中位数
  • 如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值;
  • 如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值;

 

分析

  • 如果数组在容器中已经排序,那么中位数可以由p1和p2指向的数得到;如果容器中数据的数目是奇数,那么p1和p2指向同一个数据;
  • 我们注意到整个数据容器被分割成两部分;位于容器左边部分的数据比右边数据小;另外,p1指向的数据是左边部分最大的数,p2指向 数据是左边部分最小的数;
  • 如果能保证数据容器左边的数据都小于右边的数据,那么即使左、右两边内容的数据没有排序,也可以根据左边最大的数和右边最小的数得到中位数;
  • 快速从一个数据容器中得到最大值,用大顶堆实现这个数据容器,因为位于堆顶的就是最大的数据;
  • 同样,可以快速从小顶堆得到最小值;
  • 用一个大顶堆实现左边的数据容器;用一个小顶堆实现右边的 数据容器
  • 往堆中插入一个数据的时间复杂度是O(logn);由于只需要O1)时间就可以得到位于堆顶的数据,因此得到中位数的时间复杂度是O1
  • 下表总结了使用没有排序的数组,排序的数组,排序的链表,二叉搜索树,AVL树,大顶堆和小顶堆几种不同数据结构的时间复杂度;

 

 

 

分析

  • 为了实现平均分配,可以在数据的总数目(当前小顶堆和大顶堆的总数目,不是数组的总数目)是偶数时把新的数据插入最小堆,否则插入最大堆
  • 还要保证最大堆中的所有数据都小于最小堆中的数据;
  • 当 当前的总数目是偶数时,按照前面的分配规则会把新的数据插入到小顶堆;如果此时的数据比大顶堆的第一个数小(也就是小于小顶堆的第一个数),怎么办?
  • 就得把该数放到大顶堆里,但是为了平衡,得删除大顶堆的第一个数,放到小顶堆里;
  • 当奇数的时候,按照前面的分配规则会把新的数据插入到大顶堆;如果此时的数据比小顶堆的第一个数大,就得放到小顶堆里,然后删除小顶堆的第一个元素,放到大顶堆里,为了平衡;
  • 如果是偶数,就返回大小顶堆的第一个数,/2
  • 如果是奇数,就返回小顶堆的第一个数

 

 

 

分析

  • 所谓数据流,就是不会一次性读入所有数据,只能一个一个读取,每一步都要求能计算中位数
  • 定义两个全局变量,大(重写compart方法)小顶堆
  • 小根堆是右边,大根堆是左边
  • 1、插入insert()方法:
    • 先判断是偶数还是奇数
      • 偶数(放到小根堆)
      • 当大根堆不为空的时候,才会有比较
      • 如果大根堆不为空,并且该数 《 大根堆的第一个数(maxHeap。peek 》 num)
        • 就得把这个数放到大根堆里
        • 同时从大根堆里删除一个,这个数是要放进小根堆里的
      • 把该数放到小根堆里(可能是原数,也可能是大根堆里的数)
    • 否则
      • 奇数(放到大根堆)
      • 当小根堆不为空的时候,才会有比较
      • 如果小根堆不为空,并且该数  》  小根堆的第一个数(minHeap。peek 《 num)
        • 就得把该数放到小根堆里
        • 同时从小根堆里删除一个,放到大根堆里(为了左右平衡)
      • 把该数放到大根堆里(可能是原数,也可能是小根堆里的数)
  • 2、得到中位数getMedian():
    • 鲁棒性检查(两个根加起来的长度不能等于0)
    • 定义一个变量存中位数(double)
    • 偶数
      • 得到两个根的第一个元素
      • 取中间值
    • 奇数
      • 得到小顶堆的第一个元素
    • 最后返回中位数

 

 

 

 

 

 

 

package No15_优化空间和时间效率;

 

import java.util.Comparator;

import java.util.PriorityQueue;

 

//数据流中的中位数

//因为是流,所以只能一个一个的读取,依次求其中位数

public class StreamMedian {

 

//定义两个堆,一个大根堆,一个小根堆

//小根堆,因为默认就是小根堆

PriorityQueue<Integer> minHeap = new PriorityQueue();

//大根堆Comparator.reverseOrder(),或者是自己重写compare方法

PriorityQueue<Integer> maxHeap = new PriorityQueue(Comparator.reverseOrder());

PriorityQueue<Integer> maxHeap1 = new PriorityQueue(){

public int compare(int i1,int i2){

return i2 - i1;

}

};

 

public void insert(int num){

 

//如果是偶数

if(((minHeap.size()+maxHeap.size())&1) == 0){

 

//查看是否可以直接插入到小根堆里

//如果这个数小于大根堆里的一个数,就得放到大根堆里,同时大根堆得删除一个,放到小根堆里

if(!maxHeap.isEmpty() && maxHeap.peek() > num){

maxHeap.offer(num);

num = maxHeap.poll();

}

//到最后,无论如何都得插入一个数到小根堆里,为了平衡

minHeap.offer(num);

}

//奇数,插入到大根堆里

else{

if(!minHeap.isEmpty() && minHeap.peek() < num){

minHeap.offer(num);

num = minHeap.poll();

}

maxHeap.offer(num);

}

}

 

//找中位数

public int getMedian(){

//鲁邦性检查

if((minHeap.size() + maxHeap.size()) == 0){

return -1;

}

//偶数

if(((minHeap.size()+maxHeap.size())&1) == 0){

int n1 = minHeap.peek();

int n2 = maxHeap.peek();

return (n1+n2)/2;

}

//奇数

else{

return minHeap.peek();

}

}

 

 

// ====================测试代码====================

    void test(String testName,int expected){

        if(testName != null)

            System.out.printf("%s begins: ", testName);

 

        if(getMedian()==expected)

            System.out.printf("Passed.\n");

        else

            System.out.printf("FAILED.\n");

    }

 

    public static void main( String args[]){

        StreamMedian sm=new StreamMedian();

        sm.insert(5);

        sm.test("Test1",  5);//5

 

        sm.insert(2);

        sm.test("Test2", 3);//2 5

 

        sm.insert(3);

        sm.test("Test3",  3);//2 3 5

 

        sm.insert(4);

        sm.test("Test4",  3);// 2 3 4 5

 

        sm.insert(1);

        sm.test("Test5",  3);//1 2 3 4 5

 

        sm.insert(6);

        //这个的中位数应该是3

        sm.test("Test6",  4);// 1 2 3 4 5 6

 

        sm.insert(7);

        sm.test("Test7",  4); // 1 2 3 4 5 6 7

 

        sm.insert(0);

        sm.test("Test8",  4);// 0 1 2 3 4 5 6 7

 

        sm.insert(8);

        //这个的中位数应该是3

        sm.test("Test9",  4);//0 1 2 3 4 5 6 7 8

    }

 

}

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值