堆 二叉堆 找流的中位数

二叉堆

堆排序

找流的中位数

public class BinHeap {
    long[] a;
    int size = 16;          //数组大小
    int count;              //数据个数
    boolean isMax;          //是否是大根堆

    public BinHeap() {
        a = new long[size];
    }

    public BinHeap(int size, boolean isMax) {
        this.size = size;
        this.isMax = isMax;
        a = new long[size];
    }

    public void add(long k) {
        reSize();
        a[count + 1] = k;
        count++;
        swimUp(count);
    }

    //动态扩容
    private void reSize() {
        if (count < size - 1)
            return;
        long[] a2 = new long[size << 1];
        System.arraycopy(a, 0, a2, 0, a.length);
        a = a2;
        size <<= 1;
    }

    public long pop() {
        if (count > 0) {
            long n = a[1];
            a[1] = a[count];
            count--;
            sinkDown(1);
            return n;
        }
        throw new RuntimeException("null heap");
    }

    public boolean isEmpty() {
        return count == 0;
    }

    public long top() {
        if (count > 0)
            return a[1];
        throw new RuntimeException("null heap");
    }

    public int getCount() {
        return count;
    }

    private void swimUp(int i) {
        long tmp = a[i];
        int c = i;
        int p = i;
        for (; (c >> 1) > 0 && isMax ? (a[c >> 1] < tmp) : (a[c >> 1] > tmp); c = p) {
            p = c >> 1;
            a[c] = a[p];    //空节点上移, child = parent
        }
        a[p] = tmp;         //填充空节点
    }

    private void sinkDown(int i) {
        long tmp = a[i];
        int p = i;
        int lc, rc, tc = i; //left child, right child, target child

        //TODO SUCK CODE!
        for (; p < (count / 2 + 1); p = tc) {
            lc = p << 1;
            rc = lc + 1;
            int tmptc;
            if (isMax) {
                if (rc > count) {
                    if (a[lc] < tmp) {
                        a[p] = a[lc];
                        tc = lc;
                    }
                } else {
                    tmptc = a[lc] < a[rc] ? rc : lc;
                    if (a[tmptc] > tmp) {
                        a[p] = a[tmptc];
                        tc = tmptc;
                    }
                }
            } else {
                if (rc > count) {
                    if (a[lc] < tmp) {
                        a[p] = a[lc];
                        tc = lc;
                    }
                } else {
                    tmptc = a[lc] > a[rc] ? rc : lc;
                    if (a[tmptc] < tmp) {
                        a[p] = a[tmptc];
                        tc = tmptc;
                    }
                }
            }
            if(p == tc)
                break;
        }
        a[tc] = tmp;
    }
}

找数据流中的中位数

public class CenterNumber {
    //          odd round         even round
    //                        /
    //                       /
    // (small) - ------------o------------>  + (big) stream numbers
    //             max heap /^ min heap
    //    small numbers    / |     big numbers
    //                     center number

    /**
     * 1.2个堆数量差 最大相差1
     * 2.最后结束时,maxHeap中所有数 都小于 minHeap中所有数, 此时maxHeap的堆顶,或minHeap的堆顶为中位数
     * 数据量为偶数个时(先从右边开始放入, minHeap),下一个数据时,放左边,依次交替
     *
     * @param a
     */
    BinHeap maxHeap;
    BinHeap minHeap;

    public void insertNum(long num) {
        //1 先满足数值大小,放入正确的堆
        //2 再满足堆数量平衡
        //2者不可弄反,否则堆数量平衡了,但最后求不到中位数
        if (((minHeap.getCount() + maxHeap.getCount()) & 1) > 0) {  //奇数,默认加到 maxHeap ,左边 , 用 > 或 < 比 == 判断往往所需cpu机器周期数更少,速度更快
            if (!minHeap.isEmpty() && minHeap.top() < num) {        //先判断,如果这个数比 右边 minHeap 中的最小还要大,就放到minHeap (右边)
                minHeap.add(num);
                num = minHeap.pop();                                //平衡数量
            }
            maxHeap.add(num);                                       //左边
        } else {                                                    //偶数,默认加到 minHeap ,右边
            if (!maxHeap.isEmpty() && maxHeap.top() > num) {
                maxHeap.add(num);
                num = maxHeap.pop();                                //平衡数量
            }
            minHeap.add(num);                                       //右边
        }
    }

    public float getMidNum() {
        long dataCount = minHeap.getCount() + maxHeap.getCount();
        if (dataCount > 0) {
            if ((dataCount & 1) > 0)                                 //奇数个数据量,从minHeap中取得
                return minHeap.top();
            return (maxHeap.top() + minHeap.top()) / 2f;              //偶数个数据量,2个中位数的平均数
        }
        return 0;
    }

    //从数据流中获取中位数
    public float centerNum(int[] datas) {
        maxHeap = new BinHeap(datas.length / 2 + 2, true);
        minHeap = new BinHeap(datas.length / 2 + 2, false);
        int c = 0;
        while (c < datas.length) {
            insertNum(datas[c++]);
        }
        return getMidNum();
    }

    public static void main(String[] args) {
        CenterNumber cn = new CenterNumber();
        int[] a = new int[]{
                1, 3, 4, 6, 5, 2
        };
        float mid = cn.centerNum(a);
        System.out.println(" mid " + mid); //3.5

        a = new int[]{
                1, 3, 4, 6, 5, 2, 7
        };
        mid = cn.centerNum(a);
        System.out.println(" mid " + mid); //4
    }
}

基于堆的优先队列的任务定时器

public class TaskTimer {
    BinHeap priorityQueue = new BinHeap(); //PriorityBlockingQueue

    public interface TaskChangeSubscriber {
        void onGetNextTask(long time);

        void onTask(long time);
    }

    TaskChangeSubscriber taskChangeSubscriber;
    Thread workThread;

    public class TaskCheck implements Runnable {
        AtomicBoolean doRun = new AtomicBoolean(true);

        @Override
        public void run() {
            while (doRun.get()) {
                if (priorityQueue.getCount() > 0) {  //PriorityBlockingQueue
                    try {
                        if (taskChangeSubscriber != null)
                            taskChangeSubscriber.onGetNextTask(priorityQueue.top());
                        Thread.sleep(priorityQueue.top() - System.currentTimeMillis());
                        if (priorityQueue.top() - System.currentTimeMillis() <= 0 && taskChangeSubscriber != null) {
                            taskChangeSubscriber.onTask(priorityQueue.pop());
                        }
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }

    TaskCheck taskCheck;

    public TaskTimer() {
    }

    public void start() {
        if (workThread != null) {
            taskCheck.doRun.set(false);
        }
        taskCheck = new TaskCheck();
        workThread = new Thread(taskCheck);
        workThread.start();
    }

    public void stop() {
        if (workThread != null) {
            taskCheck.doRun.set(false);
        }
        taskCheck = null;
        workThread = null;
    }

    public void addTask(long time) {
        System.out.println("addTask : " + time);
        priorityQueue.add(time);
    }
    //取消任务? 堆的删除算法?

    public TaskChangeSubscriber getTaskChangeSubscriber() {
        return taskChangeSubscriber;
    }

    public void setTaskChangeSubscriber(TaskChangeSubscriber taskChangeSubscriber) {
        this.taskChangeSubscriber = taskChangeSubscriber;
    }

    public static void main(String[] args) throws InterruptedException {
        TaskTimer t = new TaskTimer();
        t.setTaskChangeSubscriber(new TaskChangeSubscriber() {
            @Override
            public void onGetNextTask(long time) {
                System.out.println("TaskTimer.onGetNextTask " + time);
            }

            @Override
            public void onTask(long time) {
                System.out.println("TaskTimer.onTask " + time);
            }
        });
        t.addTask(System.currentTimeMillis() + 3000);
        t.start();

        Thread.sleep(3000*2);
        t.addTask(System.currentTimeMillis() + 3000);

        Thread.sleep(1500);
        t.addTask(System.currentTimeMillis() + 3000);
    }
}

TopK 问题是一个只有 右侧 minHeap 的中位数问题

同理 LowK 只有左侧 MaxHeap

原文地址:https://www.cnblogs.com/cyy12/p/11875085.html