Topk

在n个数中找出最大的前K个 or 在n个数中找出最小的前K个

(n>K)

1000个数中找到最大的前十个

方式1:

先排降序,前十个就是最大的。时间复杂度O(N*log~2~N)

方式2:

N个数依次插入大堆,PopK次,每次取堆顶的数据就是最大的前K个。==时间复杂度O(N+log~2~N)==(下篇证明)

==方式3:==

假设N非常大,N是10亿,内存中存不下这些数,他们存在文件中,k是100。那么方式1与方式2就都不可以用了。==时间复杂度 O(K+(N-K)log~2~K)==

10亿整数我们看看大概消耗多少空间

image-20211109004544437

1.用前K个数建立一个==K个数的小堆==

2.用剩下的N-K个数,依次跟堆顶的数据进行比较,如果比堆顶的数据大,就去替换堆顶的数据,在向下调整

3.最后堆里面K个数就是最大的K个数

image-20211109011437750

Topk打印函数TopkPrint

image-20211109095630047

void TopkPrint(int* a, int n, int k)
{
    //创建一个堆
    HP hp;
    //堆初始化
    HeapInit(&hp);
    //用前K个数建立一个K个数的小堆
    int i = 0;
    for (i = 0; i < k; i++)
    {
        HeapPush(&hp,a[i]);
    }
    //用剩下的N-K个数,依次跟堆顶的数据进行比较,如果比堆顶的数据大,就去替换堆顶的数据,在向下调整
    //替换就破坏结构了,我们用接口来实现,找到就先pop,然后push大的数就行
    for (i = k; i < n ; i++)
    {
        //堆顶元素小于数组中的数
        if (HeapTop(&hp) < a[i])
        {
            //先把堆顶的数给出掉
            HeapPop(&hp);
            //再插入这个数组中的数进堆 
            HeapPush(&hp, a[i]);
        }
    }
    //然后打印堆即可
    HeapPrint(&hp);
    HeapDestroy(&hp);
}

没有修改的接口见 算法给小码农堆魂器--铁血柔情

改掉的接口

向上调整函数

//向上调整函数
void AdjustUp(HPDataType* a, int size, int child)
{
    assert(a);
    int parent = (child - 1) / 2;
#if HEAP
    while (child > 0)
    {
        if (a[parent] < a[child])//父亲小于孩子就交换(大堆)
        {
            Swap(&a[parent], &a[child]);
            //交换好后重新称呼一下孩子与父亲
            child = parent;
            parent = (child - 1) / 2;
        }
        else
        {
            break;
        }
    }
#elif !HEAP
    while (child > 0)
    {
        if (a[parent] > a[child])//父亲大于孩子就交换(小堆)
        {
            Swap(&a[parent], &a[child]);
            //交换好后重新称呼一下孩子与父亲
            child = parent;
            parent = (child - 1) / 2;
        }
        else
        {
            break;
        }
    }
#endif // HEAP
}

向下调整函数

//向下调整函数
void AdjustDown(HPDataType* a, int size, int parent)
{
    assert(a);
    //创建一个孩子变量,有两个孩子就在这个上加1就行
    int child = parent * 2 + 1;
#if HEAP
    while (child < size)
    {
        //选大孩子
        if (child + 1 < size && a[child] < a[child + 1])
        {
            child++;
        }
        //大的孩子还大于父亲就交换
        if (a[child] > a[parent])
        {
            Swap(&a[child], &a[parent]);
            parent = child;
            child = parent * 2 + 1;
        }
        else
        {
            break;
        }
    }
#elif !HEAP
    while (child < size)
    {
        //选小孩子
        if (child + 1 < size && a[child] > a[child + 1])
        {
            child++;
        }
        //小的孩子还小于父亲就交换
        if (a[child] < a[parent])
        {
            Swap(&a[child], &a[parent]);
            parent = child;
            child = parent * 2 + 1;
        }
        else
        {
            break;
        }
    }
#endif // HEAP  
}

然后在Heap.h文件中加入

//0 大堆模式  1小堆模式
#define HEAP   0