剑指41-求数组中位数

文章目录

题目描述

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

例如,

[2,3,4] 的中位数是 3

[2,3] 的中位数是 (2 + 3) / 2 = 2.5

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/shu-ju-liu-zhong-de-zhong-wei-shu-lcof
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

题解

维护一个大顶堆和一个小顶堆。大顶堆存储较小的一半元素,小顶堆存储较大的一半元素。
每次添加元素时,根据两个顶堆的元素数量进行处理:
设小顶堆为h1,长度为m;大顶堆为h2,长度为n

  1. m == n
    插入大顶堆h2, 大顶堆h2的堆顶元素添加到小顶堆h1;//这时候看上去是插入到了大顶堆,实际上是插入到了小顶堆,因此h1 > h2

  2. m != n
    插入到小顶堆h1,并将小顶堆堆顶元素插入大顶堆h2。 //实际大顶堆增加,但因为小顶堆本来就比较大,因此会插入到小顶堆

获取中位数:
3. m == n
获取两个顶堆元素的平均值;
4. m != n
获取小顶堆的堆顶元素。

注意:这里的大顶堆和小顶堆的位置可以互换,但是必须保证大顶堆和小顶堆的堆顶为中间的数字。而哪边比较多其实无所谓。

可以使用java提供PriorityQueue来实现,内部使用堆排序,每次都是完全排序好的;
也可以自己手写堆化方法,其实也很简单。

PriorityQueue:

 Queue<Integer> h1, h2;
    public MedianFinder() {
        h1 = new PriorityQueue<>();
        h2 = new PriorityQueue<>((o1, o2) -> o2 - o1); 
    }
    public void addNum(int num) {
       if (h1.size() != h2.size()) {
           h2.offer(num);
           h1.offer(h2.poll());
       } else{
           h1.offer(num);
           h2.offer(h1.poll());
       }
    }
    public double findMedian() {
        return h1.size() == h2.size() ? (h2.peek() + h1.peek()) / 2.0 : h2.peek();
    }
}

自定义堆化方法:

 class MedianFinder {

        /** initialize your data structure here. */
        List<Integer> h1, h2;
        public MedianFinder() {
            h1 = new ArrayList<>();
            h2 = new ArrayList<>();
        }

        public void addNum(int num) {
            if (h1.size() == h2.size()) {
                h2.add(num);
                heapify2(h2.size() - 1,h2);
                h1.add(h2.remove(0));
                heapify1(h1.size() - 1, h1);
                 heapify2(h2.size() - 1,h2);
            }else{
                h1.add(num);
                heapify1(h1.size() - 1,h1);
                h2.add(h1.remove(0));
                heapify1(h1.size() - 1,h1);
                 heapify2(h2.size() - 1,h2);
            }
        }

        public double findMedian() {
            return h1.size() == h2.size() ? (h1.get(0) + h2.get(0)) / 2.0 : h1.get(0);
        }
        public void heapify1(int len, List<Integer> list) {
            for (int i = len / 2; i >= 0; i--) {
                int t = i;
                int left = i * 2 + 1, right = i * 2 + 2;
                if (left <= len) {
                    if (list.get(i) > list.get(left)) t = left;
                }
                if (right <= len) {
                    if (list.get(i) > list.get(right)) t = right;
                }
                if (t != i) {
                    int temp = list.get(t);
                    list.set(t, list.get(i));
                    list.set(i, temp);
                }
            }
        }
        public void heapify2(int len, List<Integer> list) {
            for (int i = len / 2; i >= 0; i--) {
                int t = i;
                int left = i * 2 + 1, right = i * 2 + 2;
                if (left <= len) {
                    if (list.get(i) < list.get(left)) t = left;
                }
                if (right <= len) {
                    if (list.get(i) < list.get(right)) t = right;
                }
                if (t != i) {
                    int temp = list.get(t);
                    list.set(t, list.get(i));
                    list.set(i, temp);
                }
            }
        }
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值