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 对于最小(大)堆的定义以及特点可以网上查看一下