九度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个元素[算法]

 

你可能感兴趣的:(堆排序)