对于数组{10, 4, 3, 6, 5, 8, 9, 3},希望求第k (k = 3) 大的数据。
如果用传统的思路解决这个问题:先用快速排序对整个数组排序。然后取第K个元素,这种方式的时间复杂度为O(N * logN)。如果n非常大,对整个数组排序需要使用外部排序 (内存中放不下,需要硬盘辅助排序)。如果使用大小为k的小顶堆,就有可能在内存中完成这个任务。也就是说,这个方法不仅可以降低时间复杂度,还可以降低内存的消耗。
这里用小顶堆的方式进行计算:取前面3个元素,建立一个小顶堆,然后遍历其它元素,如果某个元素比堆顶元素还要小,则丢弃该元素;如果该元素比堆顶元素大,则用它代替堆顶元素,并维护这个堆。最后,这个堆中的元素就是这个数组中最大的三个元素,这个堆顶元素就是第3大的数据。如果希望得到的是最大的3个数据,输出这个堆中的三个元素即可。
这个堆一共k个元素,所以维护一次堆的时间复杂度为O(logK)。遍历(N - k)个元素,并维护堆的时间复杂度为O((N - k) * logK)。
最好的情况,K = 1,不需要维护堆,所以时间复杂度为O(N);最差的情况,K = N,也就是用小顶堆求最小的那个元素,这时几乎所有时间都花费在建立一个大小为N的小顶堆,时间复杂度为O(N * logN)。与进行堆排序后取第K个元素的时间复杂度一样。但是如果是这种情况,用大顶堆计算,时间复杂度又是O(N);平均情况,如果K == N / 2,则时间复杂度为O(N * logN);一般情况下,k的值远小于N,所以时间复杂度为O(N * logK)。
这种求top-K的方法属于部分排序。如果k < logN,可以考虑用选择排序,如果k >= logN,可以使用本方法。
完整的代码如下:
#include <stdio.h>
#include <stdlib.h>
int arr [] = {
10, 4, 3, 6, 5, 8, 9, 3};
int sizeOfHeap = 3; //第k (k = 3) 大的数据,维护一个大小为3的小顶堆
int size = 0; //数组的大小
void swap(int i, int j) //交换数组arr中编号为i和j的两个元素
{
int temp = arr[i];
arr[i] = arr[j]