剑指Offer-题41(Java版):数据流中的中位数

参考自:《剑指Offer——名企面试官精讲典型编程题》

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

主要思路:可以把输入数据平分成两部分,左边的数据都小于右边的数据,那么即使左右两边内部的数据没有排序,也可以根据左边最大的数和右边最小的数获取中位数。关键是,怎么获取左边的最大值和右边的最小值,并且保持左右两部分的个数之差不超过1?
   可以借助最大堆和最小堆来实现,同时轮流把数据添加到左右部分。为了保证最小堆(右半部分)的所有元素都大于最大堆(左半部分)的所有元素,若当前数字本来要添加到最小堆中,但是当前数字比最大堆的最大值还小,那么,先把该数字添加到最大堆中,再把最大堆中的最大数字移出到最小堆中,这样就能保证最小堆中的所有元素都大于最大堆中的所有元素;若当前数字本来要添加到最大堆中,但是当前数字比最小堆的最小值还大,类似的,先添加该数字到最小堆,再移出最小堆的最小数字到最大堆。

关键点:最小堆,最大堆,平分数据(轮流添加)

时间复杂度:O(log(n))

public class MedianNumber
{
    private static Queue<Integer> minHeap = new PriorityQueue<>();
    private static Queue<Integer> maxHeap = new PriorityQueue<>(Comparator.reverseOrder());
    private static boolean isOddCount = false;  //数据总数目是奇数

    public static void main(String[] args)
    {
        insert(5);
        System.out.println(getMedian()); //5.0
        insert(2);
        System.out.println(getMedian());  //3.5
        insert(3);
        System.out.println(getMedian()); //3.0
        insert(4);
        System.out.println(getMedian()); //3.5
    }

    private static void insert(Integer currentNum)
    {
        //左边为最大堆,右边为最小堆
        //要维持最小堆的所有元素都大于最大堆的所有元素
        //数据总数为奇数,则添加到最大堆
        if (isOddCount)
        {
            //当前数值比最小堆的最小值大,则先添加到最小堆,
            //再取出最小堆的最小值添加到最大堆
            if (minHeap.size() > 0 && currentNum > minHeap.peek())
            {
                minHeap.offer(currentNum);
                currentNum = minHeap.poll();
            }
            maxHeap.offer(currentNum);
        }
        //数据总数为偶数,则添加到最小堆
        else
        {
            //当前数值比最大堆的最大值小,则先添加到最大堆,
            //再取出最大堆的最大值添加到最小堆
            if (maxHeap.size() > 0 && currentNum < maxHeap.peek())
            {
                maxHeap.offer(currentNum);
                currentNum = maxHeap.poll();
            }
            minHeap.offer(currentNum);
        }
        isOddCount = !isOddCount; //总数奇偶性改变
    }

    private static Double getMedian()
    {
        int allCount = minHeap.size() + maxHeap.size();
        if (allCount == 0) return 0.0;
        //数据总数为偶数,则中位数为中间两个数的均值
        if ((allCount & 1) == 0)
        {
            return (minHeap.peek() + maxHeap.peek()) / 2.0;
        } else
        {
            //最小堆元素个数比最大堆的元素个数多1个
            return (double) minHeap.peek();
        }
    }


}

猜你喜欢

转载自blog.csdn.net/m0_37862405/article/details/80313645