大小顶锥动态求解中位数以及如何动态求解TopK

问题一:一个有序数组(从大到小) 长度为13  中位数为 18 

int arr[] = new int[]{1, 3, 5, 6, 11, 14, 18, 21, 27, 29, 31, 56, 59}

但是我们这个数组是动态的,每次都插入一个数据,这样没插入依次中位数就要破环,而且还要每次排序,这样会很慢。

我们思路是这样维护一个大小,顶堆。 大顶堆存放0-5 元素,小顶堆 存放 6到12 ,这样小顶堆的堆顶就是中位数。

 

为题二:如何求解一个动态数组中,取出排名前k的数据;举一个现实问题,百度文章搜索量如何求动态求排名前10位的文章(文中的搜索量是不断变化的,你买次都遍历求topK是非常耗时间的)

大顶堆:堆顶元素是整个集合中最大的元素,同理 小顶堆是整个集合最小的元素。

这里 我们采用数组形式实现顶锥(另一种实现方式是链表形式)

大顶堆代码:

package com.jxd.test;

public class BigHeap {

    private int heapCont[];

    /**
     * 堆中能存储的最大数据个数
     */
    private int tab;

    /**
     * 堆中已经存在数据的个数
     */
    private int count;

    public BigHeap(int capacity) {
        heapCont = new int[capacity + 1];//因为0号位没法使用
        tab = capacity;
        count = 0;
    }

    /**
     * n表示原数组中有数据个数
     *
     * @param heapCont
     * @param n
     */
    public BigHeap(int[] heapCont, int n) {
        this.heapCont = heapCont;
        tab = heapCont.length;
        count = n;
        blindHeap(count);
    }

    /**
     * 向堆中插入数据
     * 从下往上法
     */
    public boolean insert(int data) {

        if (count >= tab) return false;//堆中数据满了

        heapCont[++count] = data;//++count 这里注意下,实际是先自增。
        int i = count;
        int index = 0;
        while ((index = i / 2) > 0 && heapCont[i] >= heapCont[index]) {
            swap(heapCont, i, index);
            i /= 2;
        }
        return true;
    }


    /**
     * 建堆的过程,把一个原数组建成堆
     * n表示数据的个数
     * @param n
     */
    private void blindHeap(int n) {
        for (int i = n / 2; i >= 1; i--) {
            heapify(i, n);
        }
    }

    /**
     * 获取堆顶元素
     *
     */
     int getTop() {
        if(count==0)return -1;
        return heapCont[1];
    }



    /**
     * 删除堆顶
     * 从上往下法
     */

    public int removeTop() {
        if (count == 0) return -1;
        int i = 1;
        int top=heapCont[i];
        heapCont[i] = heapCont[count--];//最后一个元素放到堆顶,然后count-- 相当于删除一个数据
        heapify(i, count);
        return top;
    }

    /**
     * 堆化
     *
     * @param i
     * @param n
     */
    private void heapify(int i, int n) {
        int index;
        while (true) {
            int max = i;
            /**
             * 实际就是判断以下左子树和右子树谁大。从而决定走哪一个
             */
            if ((index = i << 1) <= n && (heapCont[i] < heapCont[index])) //比较左子树
                max = index;  //i << 1 等于i乘以2

            if ((index = (i << 1) + 1) <= n && (heapCont[max] < heapCont[index])) //左子树和右子树比较
                max = index;

            if (max == i) break;//满足堆的数据结构,父节点大于等于子节点
            swap(heapCont, i, max);
            i = max;//继续下一次循环
        }

    }

    /**
     * 堆的排序 从小到大。
     * count表示元素的个数
     */

    public void sort() {

        int k = count;
        while (k > 1) {
            swap(heapCont, 1, k);
            k--;
            heapify(1, k);
        }
    }


    /**
     * 数组的互换方法
     * 下标source 和target互换
     *
     * @param a
     * @param source
     * @param target
     */
    private void swap(int a[], int source, int target) {
        int sindex = a[source];
        int tindex = a[target];
        a[source] = tindex;
        a[target] = sindex;
    }

    public static void main(String[] args) {
        BigHeap heap = new BigHeap(10);
        heap.insert(10);
        heap.insert(20);
        heap.insert(18);
        heap.insert(30);
        heap.insert(39);
        heap.insert(22);
        heap.insert(6);
        heap.insert(4);
        heap.insert(51);
        heap.insert(33);
        heap.removeTop();//删除顶
        heap.sort();
        heap.insert(53);
        System.out.println("");
    }
}

小顶堆代码: 和大顶堆代码一样,就把代码中的> 改成 <就可以

 

测试 代码:

 

package com.jxd.test;

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

        int arr[] = new int[]{1, 3, 5, 6, 11, 14, 18, 21, 27, 29, 31, 56, 59};//13

        BigHeap bigHeap = new BigHeap(10);
        for (int i = 0; i < 6; i++) {
            bigHeap.insert(arr[i]);
        }

        SmallHeap smallHeap = new SmallHeap(10);
        for (int i = 6; i < arr.length - 1; i++) {
            smallHeap.insert(arr[i]);
        }
        System.out.println("中位数为" + smallHeap.getTop());

        //动态维护数组中的中位数。 我们新添加一个元素 61 {1,3,5,7,6,11,14,18,21,27,29,31,56,59} 14
        int node = 7;

        if (bigHeap.getTop() >= node) {//进入大顶堆
            int top =bigHeap.removeTop();//把小大顶堆堆顶放到小顶堆中
            bigHeap.insert(node);//插入新数据
            smallHeap.insert(top);
        }
        else if (smallHeap.getTop() < node) {//进入小顶堆
            int top =smallHeap.removeTop();
            smallHeap.insert(node);
            bigHeap.insert(top);
        }

        System.out.println("添加元素"+node +"之后的中位数为" + smallHeap.getTop());
    }
}

测试结果:

中位数为18
添加元素7之后的中位数为 14

问题二:

 

 

package com.jxd.test;

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

        int arr[] = new int[]{18, 3, 9, 31, 22, 16, 15, 19, 51, 88, 14, 33, 38};//无序数组
        int k = 3;
        //比如我们排名k的数据
        SmallHeap heap = new SmallHeap(k);
        heap.insert(arr[0]);
        heap.insert(arr[1]);
        heap.insert(arr[2]);//先插入前边三个数据
        for (int i = 3; i < arr.length; i++) {
            if(heap.getTop()<arr[i]){//如果数据大于堆顶
                heap.removeTop();
                heap.insert(arr[i]);
            }
        }
        System.out.println("排名前"+k+"数据" +bigHeap.getTop());//这里你断点调试下 都在SmallHeap 数组中
        
        //动态数据 比如我们添加一个数据 位99 我们先不着急添加到数组中,先在小顶堆中看看是否大于小顶堆的 堆顶元素
        int  node =99;
        if(heap.getTop()<node){
            heap.removeTop();//删除堆顶 然后添加这个新元素
            heap.insert(node);
        }
        int arrNew[] = new int[]{18, 3, 9, 31, 22, 16, 15, 19, 51, 88, 14, 33, 38,99};//无序数组
        System.out.println("动态排名前"+k+"数据"+heap.getTop() );//这里你断点调试下 都在SmallHeap 数组中
    }
}

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章