算法——TOP K问题最小堆实现

来源:互联网 发布:非负矩阵分解 matlab 编辑:程序博客网 时间:2024/06/05 12:41

1. 问题背景

在实际应用中,我们经常会遇到在一大推数据中找出最大的几个数的问题,也就是我们提到的TOP K问题。K表示需要找出数据的数量

2. 解决方案

TOP K问题也有多种解决方案,比如排序,最后截取靠前或者靠后的K个数据。当数据量小的时候,排序解决起来当然可以,算法简单,排序算法也有很多现成的。当数据量很大时,维护一个很长的数组,不管是空间存储上还是排序耗费的时间上都可能难以接受。这时我们可以采用最小堆的方式来实现,只需要维护一个K长度的数组就行

3. 具体实现

数据结构最小堆MinHeap, 其中最重要的是堆的调整adjustHeap

package org.cyxl.common;/** * Created by jeff on 16/5/11. */public class MinHeap {    // 堆的存储结构 - 数组    private int[] data;    /**     * 初始化堆中的数据,以int的最小值作为初始值     *     * @param k     */    public MinHeap(int k)    {        this.data = new int[k];        for(int i=0;i<k;i++){            data[i]=Integer.MIN_VALUE;        }    }    private void adjustHeap(int i)    {        //获取左右结点的数组下标        int l = left(i);        int r = right(i);        // 这是一个临时变量,表示 跟结点、左结点、右结点中最小的值的结点的下标        int min = i;        // 存在左结点,且左结点的值小于根结点的值        if (l < data.length && data[l]<data[i]){            min = l;        }        // 存在右结点,且右结点的值小于以上比较的较小值        if (r < data.length && data[r]<data[min]){            min = r;        }        // 左右结点的值都大于根节点,直接return,不做任何操作        if (i == min)            return;        // 交换根节点和左右结点中最小的那个值,把根节点的值替换下去        swap(i, min);        // 由于替换后左右子树会被影响,所以要对受影响的子树再进行adjustHeap        adjustHeap(min);    }    /**     * 获取右结点的数组下标     * @param i     * @return     */    private int right(int i)    {        return (i + 1) << 1;    }    /**     * 获取左结点的数组下标     * @param i     * @return     */    private int left(int i)    {        return ((i + 1) << 1) - 1;    }    /**     * 交换元素位置     *     * @param i     * @param j     */    private void swap(int i, int j)    {        int tmp = data[i];        data[i] = data[j];        data[j] = tmp;    }    /**     * 将数值加入到堆中     *     * @param element     */    public void add(int element)    {        if(element>data[0]) {            data[0] = element;            adjustHeap(0);        }    }    public int[] getData(){        return data;    }    @Override    public String toString(){        StringBuilder builder = new StringBuilder();        builder.append("{");        for (int i=0;i<data.length;i++){            builder.append(data[i]);            if(i!=data.length-1){                builder.append(",");            }        }        builder.append("}");        return builder.toString();    }}

4. 测试

public static void main(String[] args){        int[] a = {12,1,5425,12,43,734,343,999,8,99,55,2,222,444,11};        //求最大的10个        int k=10;        MinHeap heap = new MinHeap(k);        for(int i:a){            heap.add(i);        }        System.out.print(heap);    }

输出结果是

{12,43,55,99,222,343,999,734,5425,444}

5. 扩展

5.1 当然实际应用中,我们找的可能不是单纯的数字。更可能是和这些数字相关的某些对象。比如找出年龄最大的10个用户,这时我们的User对象可以实现Comparable接口来实现比较。

5.2 这里我们是最小堆来实现TOP K问题,当然也可以用最大堆来找出最小的K个值

5.3 对于最小(大)堆的定义以及特点可以网上查看一下

0 0
原创粉丝点击