九度OJ 1371 最小的K个数 -- 堆排序

题目地址:http://ac.jobdu.com/problem.php?pid=1371

 

题目描述:

输入n个整数,找出其中最小的K个数。例如输入4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4,。

输入:

每个测试案例包括2行:

第一行为2个整数n,k(1<=n,k<=200000),表示数组的长度。

第二行包含n个整数,表示这n个数,数组中的数的范围是[0,1000 000 000]。

输出:

对应每个测试案例,输出最小的k个数,并按从小到大顺序打印。

样例输入:
8 4
4 5 1 6 2 7 3 8
样例输出:
1 2 3 4

 

我们可以先创建一个大小为k的数据容器来存储最小的k个数字。接下来我们每次从输入的n个整数中读入一个数。如果容器中已有的数字少于k个,则直接把这次读入的整数放入容器之中;如果容器中已有k个数字了,也就是容器已满,此时我们不能再插入新的数字而只能替换已有的数字。我们找出这已有的k个数中最大值,然和拿这次待插入的整数和这个最大值进行比较。如果待插入的值比当前已有的最大值小,则用这个数替换替换当前已有的最大值;如果带插入的值比当前已有的最大值还要大,那么这个数不可能是最小的k个整数之一,因为我们容器内已经有k个数字比它小了,于是我们可以抛弃这个整数。

因此当容器满了之后,我们要做三件事情:一是在k个整数中找到最大数,二是有可能在这个容器中删除最大数,三是可能要插入一个新的数字,并保证k个整数依然是排序的。如果我们用一个二叉树来实现这个数据容器,那么我们能在O(logk)时间内实现这三步操作。因此对于n个输入数字而言,总的时间效率就是O(nlogk)

下面是用堆排序解决该问题的代码:

 

#include <stdio.h>
  
void HeapSort(int Array[], int len);
void BuildMaxHeap(int Array[], int len);
void ShiftDown(int Array[], int index, int len);
void Swap(int *m, int *n);
  
int main(void){
    int len;
    int k;
    int Array[200000];
    int i;
  
    while (scanf("%d %d", &len, &k) != EOF){                 //读入数据个数
        for (i=1; i<=len; ++i)             //读入数据
            scanf("%d", &Array[i]);
        BuildMaxHeap(Array, k);
        for (i=k + 1; i <= len; ++i){
            if (Array[i] < Array[1]){
                Array[1] = Array[i];
                ShiftDown(Array, 1, k);
            }
        }
        HeapSort(Array, k);
        for (i=1; i<=k; ++i){
            printf("%d", Array[i]);
            if (i != k)
                putchar (' ');
        }
        putchar ('\n');
    }
    return 0;
}
  
void HeapSort(int Array[], int len){
    int i;
    int time = len;
    for (i=0; i<len; ++i){
        Swap(&Array[1], &Array[time]);
        --time;
        ShiftDown(Array, 1, time);
    }
}
  
void BuildMaxHeap(int Array[], int len){
    int index = len/2;
  
    for (; index>0; --index){
        ShiftDown(Array, index, len);
    }
}
void ShiftDown(int Array[], int index, int len){
    while ((2*index) <= len || (2*index+1) <= len){
        if (2*index+1 <= len){
            if (Array[index] < Array[2*index] ||
                Array[index] < Array[2*index+1]){
                if (Array[2*index] > Array[2*index+1]){
                    Swap(&Array[index], &Array[2*index]);
                    index = 2*index;
                }
                else{
                    Swap(&Array[index], &Array[2*index+1]);
                    index = 2*index + 1;
                }
            }
            else
                break;
        }
        else{
            if (Array[index] < Array[2*index]){
                Swap(&Array[index], &Array[2*index]);
                index = 2*index;
            }
            else
                break;
        }
    }
}
  
void Swap(int *m, int *n){             //交换数据
    int tmp;
    tmp = *m;
    *m = *n;
    *n = tmp;
}

 

 

清橙OJ上相似的题目:http://blog.csdn.net/jdplus/article/details/19675351

九度OJ上相似的题目:http://blog.csdn.net/jdplus/article/details/19677345

参考资料:何海涛 -- 程序员面试题精选100题(05)-查找最小的k个元素[算法]

 

posted @ 2014-02-11 12:32  liushaobo  阅读(387)  评论(0编辑  收藏  举报