剑指offer第六十三题数据流中的中位数

题目描述

如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。我们使用Insert()方法读取数据流,使用GetMedian()方法获取当前读取数据的中位数。

方法一:大小堆

1、什么是堆?(参考:https://www.cnblogs.com/lanhaicode/p/10546257.html

堆是一种非线性结构,可以把堆看作一个数组,也可以被看作一个完全二叉树,通俗来讲堆其实就是利用完全二叉树的结构来维护的一维数组

按照堆的特点可以把堆分为大顶堆小顶堆

大顶堆:每个结点的值都大于等于其左右孩子结点的值

小顶堆:每个结点的值都小于等于其左右孩子结点的值

(图片来源:https://www.cnblogs.com/chengxiao/p/6129630.html)

我们对堆中的结点按层进行编号,将这种逻辑结构映射到数组中就是下面这个样子

我们用简单的公式来描述一下堆的定义就是:(读者可以对照上图的数组来理解下面两个公式)

大顶堆:arr[i] >= arr[2i+1] && arr[i] >= arr[2i+2] 

小顶堆:arr[i] <= arr[2i+1] && arr[i] <= arr[2i+2] 

堆排序见我之前的文章——十大经典排序算法

按这个写:

import java.util.*;

class Solution {
    Queue<Integer> left = new PriorityQueue<>((o1, o2) -> (o2 - o1));大顶堆
    Queue<Integer> right = new PriorityQueue<>((o1, o2) -> (o1 - o2));小顶堆
    int count = 0;
    public void Insert(Integer num) {
        count++;
        //边界条件
        if (count % 2 == 1) {
            left.add(num);
            right.add(left.poll());
        } else if (count % 2 == 0) {
            right.add(num);
            left.add(right.poll());
        }
    }

    public Double GetMedian() {
        if (count % 2 == 1) {
            return (double)(right.peek());
        } else if (count % 2 == 0) {//注意:这里如果是else,下面就不用加0.0了
//            return (double)((left.peek() + right.peek())/2);//注意这里不能除2,会变成商的,应该除以2.0
            return (left.peek() + right.peek())/2.0;
        }
        return 0.0;
    }
}

public class Main {
    public static void main(String[] args) {
        int[] nums = {7, 5, 4, 2, 1, 6, 3};
        Solution p = new Solution();
        for (int i = 0; i < nums.length; i++) {
            p.Insert(nums[i]);
        }
        Double res = p.GetMedian();
        System.out.println("序列化" + res);
    }
}

原来的:

package test;
import java.util.PriorityQueue;
import java.util.Queue;

class Solution {
    /* 大顶堆,存储左半边元素 */
    Queue<Integer> left;
    /* 小顶堆,存储右半边元素,并且右半边元素都大于左半边 */
    Queue<Integer> right;
    public void MedianFinder() {
        /* 大顶堆,存储左半边元素 */
        // B = new PriorityQueue<>((x, y) -> (x - y)); 是小顶堆,顶部最小的数
        // B = new PriorityQueue<>((x, y) -> (y - x)); 是大顶堆,顶部最大的数
        left = new PriorityQueue<>((o1, o2) -> (o2 - o1));
        /* 小顶堆,存储右半边元素,并且右半边元素都大于左半边 */
        right = new PriorityQueue<>();//小顶堆
    }

    /* 当前数据流读入的元素个数 */
    private int N = 0;
    public void Insert(Integer num) {
        /* 插入要保证两个堆存于平衡状态 */
        if (N % 2 == 0) {
            /* N 为偶数的情况下插入到右半边。
             * 因为右半边元素都要大于左半边,但是新插入的元素不一定比左半边元素的大,(就是这么理解,
             * 最大堆第一个放最大值,最小堆第一个放最小值)
             * 因此需要先将元素插入左半边,然后利用左半边为大顶堆的特点,取出堆顶元素即为最大元素,此时插入右半边
             */
            left.add(num);
            right.add(left.poll());
        } else {
            right.add(num);
            left.add(right.poll());
        }
        N++;
    }

    public Double GetMedian() {
        if (N % 2 == 0)
            return (left.peek() + right.peek()) / 2.0;
        else
            return (double) right.peek();
    }
}

public class Main {
    public static void main(String[] args) {
        //               3 1 2
        int[] temp = {1,2,5,7,3,4};//1 2 3 4 5 7    4 7 5
        Solution p = new Solution();
        p.MedianFinder();
        for(int i = 0; i < temp.length; i++){
            p.Insert(temp[i]);
        }
        System.out.println("输出大顶堆插入结果"+p.left);
        System.out.println("输出小顶堆插入结果"+p.right);
        Double b = p.GetMedian();
        System.out.println("输出中位数"+b);
    }
}

方法二:首先是由Collection.sort()进行升序排列,然后return中位数呗。

package test;
import java.util.Collections;
import java.util.ArrayList;
class Solution {
    ArrayList<Integer> list = new ArrayList<>();
    public void Insert(Integer num) {
        list.add(num);
        Collections.sort(list);//对list进行升序排序
    }

    public Double GetMedian() {
        int n = list.size();
        if (n % 2 == 0) {
            return Double.valueOf((list.get(n / 2) + list.get(n / 2 - 1)) / 2.0);
        } else {
            return Double.valueOf(list.get(n / 2));
        }
    }
}

public class Main {
    public static void main(String[] args) {

        int[] temp = {1,2,5,7,3,4};
        Solution p = new Solution();
        for(int i = 0; i < temp.length; i++){
            p.Insert(temp[i]);
        }
        System.out.println("输出插入结果"+p.list);

        Double b = p.GetMedian();
        System.out.println("输出中位数"+b);
    }
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值