zoukankan      html  css  js  c++  java
  • 堆 二叉堆 找流的中位数

    二叉堆

    堆排序

    找流的中位数

    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

  • 相关阅读:
    历史上的今天:3月1日
    用Windows Live Writer远程更新wordpress博客
    二月只有28天
    办公也可云概念
    FLASH做的画图板
    FLASH鼠标跟随特效
    silverlight应该以什么面孔见人?
    飞信Fetion 开发资料及下载
    收藏一个数学的C++算法的好博客
    下载网站资源共享
  • 原文地址:https://www.cnblogs.com/cyy12/p/11875085.html
Copyright © 2011-2022 走看看