zoukankan      html  css  js  c++  java
  • 剑指offer--数据流中的中位数

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

    import java.util.*;

    /*

    方法一:

    public class Solution {

             ArrayList<Integer> list=new ArrayList<>();

        public void Insert(Integer num) {

                 list.add(num);

        }

     

        public Double GetMedian() {

            Collections.sort(list);

            int size=list.size();

            double s;

            if(size%2==0){

                s= ((double)list.get(size/2-1)+(double)list.get(size/2))/2;

            }else

                s= list.get(size/2);

           

            return s;

        }

     

     

    }

    */

    方法二:

     作者用到的方法非常巧妙,注意到我们只需要位于中间的两个数就可以得出中位数,当元素个数为奇数个时可以看成这两个数一样。中位数在前半部分它是最大的,右半部分是最小的。我们只要始终知道前半部分数里的最大的那个数,后半部分里那个最小的数就可以了,其他的不用关心。这明显是堆的功用,前半部分用一个最大堆,后半部分建一个最小堆。中位数要保持两个堆的数目之差不超过1.为了实现平均分配,可以在数据的总数目是偶数时把新数据插入到最小堆中,否则插入最大堆中。

        另一个问题是保持最大堆的所有数据都小于最小堆中的数据。如果总数目和为偶数,根据之前的原则应该插入最小堆中,但是新插入的元素可能比最大堆中的元素小怎么办?这就违反了最小堆的数据一定大于最大堆得原则。实际上这里插入的元素应该是新元素和最大堆中所有元素的最大值,最大堆正好有返回一个最大值的作用,所以新插入的元素先插入最大堆,然后取出最大值再插入最小堆中。如果总数目和为奇数,处理方法类似。

    /**

             思路:中位数就是数据前一部分的最后一个(最大值)和后半部分的第一个(最小值)的平均。当元素个数为奇数个时可以看成这两个数一样

        这里使用两个堆,一个大根堆,保存前半部分数据,小根堆保存后半部分数据。小根堆数据大于大根堆

        为了实现平均分配,可以在数据的总数目是偶数时把新数据插入到最小堆中(此时总数+1),否则插入最大堆中。这样也保证了两个堆的数目相差不超过1

        最后的结果就是:如果总数是偶数个时,就是小根堆的最小值和大根堆的最大值的平均。

        如果是奇数个时,因为添加最后一个元素之前是偶数个,所以会将最后一个添加到小根堆中,所以返回小根堆最小值。

       

        注意:在新插入一个数据时,假设之前总数是偶数,根据自定义规则,这时会将新元素加入到小根堆中,但是此时不能保证此元素会比大根堆的数据大。

        所以此时加入小根堆的是新元素和大根堆所有元素的最大值,而不是新元素

        同理,如果当前总数是奇数,就会将新元素和小根堆所有元素的最小值加入大根堆。

    */

    public class Solution {

        /*

        java中默认的堆是小根堆,要实现大根堆可以自己设置比较方式

        */

        int count=0;

             PriorityQueue<Integer> minQue=new PriorityQueue<>();

        PriorityQueue<Integer> maxQue=new PriorityQueue<Integer>(11,new Comparator<Integer>(){

            public int compare(Integer o1,Integer o2){

                return o2-o1;

            }

        });

        public void Insert(Integer num) {

                 /*

                     当当前数据总数为偶数时,将新元素和大根堆所有元素的最大值加入小根堆。

                当当前数据总数为奇数时,将新元素和小根堆所有元素的最小值加入大根堆。

            */

            if(count%2==0){

               

                    maxQue.offer(num);

                    minQue.offer(maxQue.poll());

                   

               

            }else{

                minQue.offer(num);

                maxQue.offer(minQue.poll());

            }

            count++;

        }

     

        public Double GetMedian() {

           if(count%2==0){

               return new Double((minQue.peek()+maxQue.peek()))/2;

           }else

               return new Double(minQue.peek());

        }

     

     

    }

  • 相关阅读:
    跨控制器跳转view——RedirectToRoute和RedirectToAction
    Web GIS离线解决方案
    逆变与协变详解
    C#操作符??和?:
    DBNull与Null的区别
    C#用DataTable实现Group by数据统计
    LINQ系列:LINQ to DataSet的DataTable操作
    Repeater数据控件的两个重要事件ItemDataBound 和 ItemCommand
    Visual Studio提示“无法启动IIS Express Web服务器”的解决方法
    ADO.NET 数据库备份等操作
  • 原文地址:https://www.cnblogs.com/xiaolovewei/p/8028810.html
Copyright © 2011-2022 走看看