1. 描述
输入 n 个整数,输出其中最小的 k 个。
2. 解法
一般的方法是用选择排序,直到k个最小的数产生。时间复杂度为O(nk),空间复杂度为O(n)。
或者是对这n个数用快速排序从小到大排好序后,输出前k个数,时间复杂度为O(nlogn),空间复杂度为O(n)。
但若数据量非常庞大,以至于整个内存空间都放不下,上面两种方法就无能为力了。
利用堆排序可以解决空间复杂度的问题,用O(k)的空间复杂度就可以解决。
下面的算法输入n个数,打印出最小的k个数。
(1) 若 k >= n,直接打印出这n个数,否则执行第(2)步。
(2) 把输入的前k个数放到A[1..k]中,创建这n个数的大顶堆,则A[1]为这 k 个数中最大的数。
(3) 若已输入的数的个数等于n执行第(5)步,否则输入下一个数。比较这个数与A[1]的大小,若这个数大于等于A[1],重新返回(3)。否则执行(4)。
(4) 令A[1]等于这个数,调整这k个数使其重新成为大顶堆。返回第(3)步。
(5) 结束循环,打印A[1..k]。程序结束。
代码如下:
void min_k_nums(int n, int k)
{
int A[k + 1], i, j, temp;
printf("input %d numbers: \n", n);
for (i = 1; i <= n; i++)
{
if (i <= k) { //前k个数直接存放
scanf("%d", &A[i]);
continue;
}
if (i == k + 1) {
for (j = k >> 1;j >= 1; j--) //对前k个数创建大顶堆
heapAdjust(A, j, k);
}
scanf("%d", &temp);
if (temp < A[1]) { //比较输入的数与堆顶元素的大小
A[1] = temp;
heapAdjust(A, 1, k); //调整使其重新成为大顶堆
}// if
}// for
printf("The least k numbers are: \n");//输出结果
for (i = 1;i <= k; i++)
printf("%d ", A[i]);
printf("\n");
}
其中调整成为大顶堆的算法如下:
void heapAdjust(int A[], int s, int m)
{
int j, rc;
rc = A[s];
for (j = s << 1; j <= m; j <<= 1)
{
if (j < m && A[j] < A[j + 1])
j++;
if (rc >= A[j])
break;
A[s] = A[j];
s = j;
}
A[s] = rc;
}
此算法见<堆排序>。
测试结果如下: