1. 程式人生 > >Top K問題——基於堆排序

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”