大小堆排序 & Top K 问题

时间:2021-11-01 16:23:23

大小堆排序

堆这种数据结构定义比较简单,大根堆就是父节点的值大于左右子孩子节点的值(小根堆相反),而且利用数组下标就可以很好的表现堆(不过要注意是从0 还是 1开始)。堆常用语实现优先队列,Top K等问题。

算法导论第6章对堆的进行了详细讲解,我就不赘述了(看书是不够的,要把思路用代码实现出来才是真的懂了,争取把算法导论上常用的数据结构和算法都自己实现一般)。

大根堆

具体代码(按照算法导论中下标从1开始):

/******* 大小堆排序 & topk问题 ***********/

//调整大根堆(大堆化),数组从1下标开始
void adjust_maxheap(int a[],int size,int i)
{

    int left = i*2; //左节点
    int right = i*2+1; //右节点
    int max_index;

    //找根,左,右中最大的节点
    if(left < size && a[left]>a[i])
        max_index = left;
    else
        max_index = i;

    if(right < size && a[right] > a[max_index])
        max_index = right;

    //如果原来的根不是最大值,则需要交换,交换后原来max值的节点变成了比较小的根
    if( i != max_index ) 
    {
        SWAP(a[i],a[max_index]);
        adjust_maxheap(a,size,max_index);
    }
}


//建立大根堆
void build_maxheap(int a[],int size)
{
    int start = size>>1; //第一个非叶子节点
    int i ;
    for(i = start;i>=1;i--)
    {
        adjust_maxheap(a,size,i);
    }
}

//大堆排序
void maxheap_sort(int a[],int size)
{
    int i;
    for(i = size;i>=1;i--)
    {
        build_maxheap(a,i);
        SWAP(a[1],a[i]);
    }
}

测试代码与运行截图:

大小堆排序 & Top K 问题

小根堆

具体代码(下标从0开始):

void adjust_minheap(int a[],int size,int i)
{
    int left = i*2+1;
    int right = i*2+2;

    int min_index;
    if( left < size && a[left]<a[i])
        min_index = left;
    else
        min_index = i;

    if( right < size && a[right]< a[min_index])
        min_index = right;

    if( min_index != i)
    {
        SWAP(a[i],a[min_index]);
        adjust_minheap(a,size,min_index);
    }
}

void build_minheap(int a[],int size)
{
    int start = size>>1;
    for(start;start>=0;start--)
    {
        adjust_minheap(a,size,start);
    }
}

void minheap_sort(int a[],int size)
{
    int i,j=size,last= size -1;
    for(i = 0;i<size;i++)
    {
        build_minheap(a,j--);
        SWAP(a[0],a[last]);
        last -= 1;
    }
}

测试代码与运行截图:

大小堆排序 & Top K 问题

运用堆排序解决Top K问题

top k问题就是在一堆数据中选择前K大(前K小)的数据。做法有许多,可以先把所有数据排序,然后选前k个。
然后用堆排序解决Top K问题则不用先全部排序,只需维护一个大小为K的堆即可。

实现思路:

如果要选出前K大,则将数据中的前K个元素建立成一个小根堆,从第K+1个元素开始往后依次比较,如果元素大于小根堆的堆顶,那么就和堆顶交换,交换后重新调整为小根堆。这样变量一遍所有数据,最后得到的大小为K的小根堆就是前K大的树。

具体代码:

void topk_biggest(int a[],int size,int k)
{
    int i;

    for(i = k;i<size;i++)
    {
        build_minheap(a,k);
        if(a[i]>a[0])
            SWAP(a[i],a[0]);
    }
    printf("最大%d个数为:",k);
    for(i =0;i<k;i++)
        printf("%d ",a[i]);
}

Top k 测试代码&运行截图:

大小堆排序 & Top K 问题