/* All Contributors (C) 2020 */
package io.github.dreamylost.practice;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.PriorityQueue;

/**
 * @description 如何得到一个数据流中的中位数？ 如果从数据流中读出奇数个数值，那么中位数就是所有数值排序之后位于中间的数值。
 *     如果从数据流中读出偶数个数值，那么中位数就是所有数值排序之后中间两个数的平均值。
 * @author Mr.Li
 */
public class GetMedianInStream {

    private ArrayList<Integer> res = new ArrayList<Integer>();

    public void Insert(Integer num) {
        res.add(num);
        Collections.sort(res);
    }

    public Double getMedian() {
        int mid = res.size() / 2;
        if ((res.size() & 1) == 0) {
            Integer n1 = res.get(mid);
            Integer n2 = res.get(mid - 1);
            double s = (Double.valueOf(n1 + "") + Double.valueOf(n2 + "")) / 2;
            return s;
        } else {
            double s = Double.valueOf(res.get(mid) + "");
            return s;
        }
    }

    /** ****************** 使用优先级队列 ***************************** */
    private int count = 0;

    private PriorityQueue<Integer> minHeap = new PriorityQueue<>();
    private PriorityQueue<Integer> maxHeap =
            new PriorityQueue<Integer>(
                    15,
                    new Comparator<Integer>() {
                        @Override
                        public int compare(Integer o1, Integer o2) {
                            return o2 - o1;
                        }
                    });

    public void Insert2(Integer num) {
        if (count % 2 == 0) { // 当数据总数为偶数时，新加入的元素，应当进入小根堆
            // （注意不是直接进入小根堆，而是经大根堆筛选后取大根堆中最大元素进入小根堆）
            // 1.新加入的元素先入到大根堆，由大根堆筛选出堆中最大的元素
            maxHeap.offer(num);
            int filteredMaxNum = maxHeap.poll();
            // 2.筛选后的【大根堆中的最大元素】进入小根堆
            minHeap.offer(filteredMaxNum);
        } else { // 当数据总数为奇数时，新加入的元素，应当进入大根堆
            // （注意不是直接进入大根堆，而是经小根堆筛选后取小根堆中最大元素进入大根堆）
            // 1.新加入的元素先入到小根堆，由小根堆筛选出堆中最小的元素
            minHeap.offer(num);
            int filteredMinNum = minHeap.poll();
            // 2.筛选后的【小根堆中的最小元素】进入大根堆
            maxHeap.offer(filteredMinNum);
        }
        count++;
    }

    @SuppressWarnings("deprecation")
    public Double getMedian2() {
        // 当个数为偶数的时候，小根堆和大根堆的顶的一半就是中位数
        if (count % 2 == 0) {
            return new Double((minHeap.peek() + maxHeap.peek())) / 2;
        } else {
            // 当个数为奇数的时候，小根堆的堆顶就是中位数
            return new Double(minHeap.peek());
        }
    }
}
