Top K问题——基于堆排序

一、简介

所谓的Top K问题其实就是找数组中最大的前k个值。为此,只要我们能够找到数组中的第k大值,那么Top K问题就会迎刃而解。在此声明一下,本文写的方法肯定不是最好的。不过最近看了几个题,其核心都是找第k大的值。这里,我只是总结下而已。

二、举例说明

例如:对于数组a[N],取其最大的前k个数。

1、用数组b[k]建立初始小顶堆(用0初始化数组b即可);

2、从i=1,2,…,N依次遍历a:

2.1、若:a[i] > b[0],用a[i]取代b[0],同时重新调整小顶堆;

2.2、否则,保持b[0]不变。

3、重复步骤2,一直到i=N为止。

三、详细代码

3.1、小顶堆

#define NUM 10
typedef int ELEM;
void heap(ELEM a[],int left,int right)
{
    if (left >= right) 
            return ;
    int r = left;//指向当前的根结点
    int LChild = 2*r+1;//指向当前跟结点的左孩子
    int Child = LChild;

    ELEM temp = a[r];//记录当前根结点元素

    //开始逐渐向下调整
    while(LChild <= right)
    {
        if(LChild < right && a[LChild] > a[LChild+1])
                    Child = LChild + 1;//Child指向子节点中最小的那个
        if(temp > a[Child]) 
        {
                a[r] = a[Child];                    
                r = Child;//重新调整跟结点指向
                LChild = 2*r+1;//重新调整左孩子指向
                Child = LChild;//重新调整孩子指向
        }
        else
            break;
    }
    a[r] = temp;
    return;
}

3.2、主函数

int main(int argc,char **argv) 
{   
    if(argc < 2)
    {
        printf("参数数量不够!");
        return 0;
    }   
    int i;
    int k = atoi(argv[1]);
    ELEM a[NUM] = {2,5,3,1,6,13,15,1859,131,13};

    ELEM* b = (ELEM*)malloc(k*sizeof(ELEM));    
    memset(b,0,k*sizeof(ELEM));

    //核心部分
    for(i = 0;i < NUM;i++)
    {
            if(a[i] > b[0])
            {
                    b[0] = a[i];
                    heap(b,0,k-1);
            }
    }

    //如果还要求最大的k个数有序,将数组b(堆)排序即可.
    printf("a中最大的k个数:");
    for (i = 0;i < k;i++)
        printf("%d ",b[i]);     

    free(b);
    b = NULL;

    return 0;
}

3.3、输出结果

k=5时,上述代码输出结果为:

a中最大的k个数:13 13 131 1859 15

(这里的k个数是无序的)

4、说明

如果要取前k个最小的数,将上述小顶堆改为大顶堆再将主函数中

if(a[i] > b[0])
{
        b[0] = a[i];
        heap(b,0,k-1);
}

的“a[i] > b[0]“改为”a[i] < b[0]“即可。


上述思想主要参考自“编程之美,p142~p144”

猜你喜欢

转载自blog.csdn.net/jnulzl/article/details/51073755
今日推荐