最小的k个数

来源:互联网 发布:天天向上网络女神 编辑:程序博客网 时间:2024/06/04 18:47

题目:输入n个数,找出其中最小的n个数。例如输入4,5,1,6,2,7,3,8这8个数字,则最小的4个数字为1,2,3,4.

拿到这道题最显而易见的思路就是把这几个数排序,然后直接输出前k个数就OK了。用快排的话,它的时间复杂度为O(nlogn),但还有更快的算法。

我们可以借助快速排序中的partition函数来完成,这个函数是用来将一个数组中的数分为两部分,左边的数均小于给定的值,右边的数均大于给定的值,我在这里贴出partition的算法:

void Swap(int *e1, int *e2){int tmp = *e1;*e1 = *e2;*e2 = tmp;}int Media_3(int arr[], int start, int end){//返回头中尾三个数的中位数的下标int mid = (end - start) / 2 + start;if (arr[start] > arr[mid])Swap(&arr[start], &arr[mid]);if (arr[start] > arr[end])Swap(&arr[start], &arr[end]);if (arr[mid] > arr[end])Swap(&arr[mid], &arr[end]);return mid;}int partition(int arr[], int len, int start, int end){int small, index;if (NULL == arr || len <= 0 || start < 0 || end >= len)return -1;index = Media_3(arr, start, end);Swap(&arr[index], &arr[end]);small = -1;for (index = start; index < end; ++index){if (arr[index] < arr[end]){++small;if (index != small)Swap(&arr[index], &arr[small]);}}++small;Swap(&arr[small], &arr[end]);return small;}

partition返回的是一个下标值,并且,在这个数组中,此下标左边的值均小于这个下标位置对应的值,右边的值均大于这个下标对应的值,即将数组分成了两部分,左边小,右边大,分界线为这个下标对应的值。

有了这个函数,那么当我们需要查找最小k个数的时候,就可以想到,当partition返回的下标值为k-1时,那么左边的数必然均小于等于第k个最小的数,这时我们从下标0至k-1的k个数就是我们要查找的数了。下面是具体实现:

void SmallestKNumber(int input[], int n, int output[], int k){int index;int start = 0, end = n - 1;if (NULL == input || n <= 0 || NULL == output || k <= 0 || k >= n)return;index = partition(input, n, start, end);while (index != k - 1){if (index < k - 1){start = index + 1;index = partition(input, n, start, end);}else{end = index - 1;index = partition(input, n, start, end);}}for (index = 0; index < k; ++index)output[index] = input[index];}

当然采用这种思路是有所限制的,因为它改变了原先的数组,所以决定用这种方法前,最好想清楚。还有一种效率略低一点的算法,以后再来讲。

1 0