TOP K即返回给定集合最大的K个元素,这个集合有可能很大,十亿,有可能万亿,所以对算法的要求比较高。以下是我的总结:
一、采用快速排序的分治算法思想进行求解:
快速排序的思想是使用一个标志点将数组分为两个部分,小于该点的数据移动到该点的左侧,大于该点的数据移动到该点的右侧,然后进行递归,最后达到有序。同理我们也可以使用该思想求数组的TOP K。也是使用第一个元素左右标志,小于该点的元素移到左侧,大于该点的元素移到右侧,第一次partition后有有三种情况:
1、标志点右侧的数据正好是K-1,那么加上标志点就是要求的TOP K个元素
2、标志点右侧的数据个数N小于K-1,那么就需要在标志点左侧的集合中寻找TOP (K- N),通过递归就可以实现
3、标志点右侧的数据个数N大于K-1,那么就需要在标志点右侧的集合中需要TOP K个元素,通过递归实现
代码如下:
#include <stdio.h> #include <unistd.h> void getRand(int* data, int length) { int i; srand((unsigned int) getpid()); for (i = 0; i < length; i++) { data[i] = rand() % 1000; } //print the array printf("generate the array using rand:\n"); for (i = 0; i < length; i++) { printf("%d ", data[i]); } printf("\n"); } void partition(int arr[], int low, int high, int *pos){ int key = arr[low]; int i = low, j = high; while(i < j){ while(i < j && arr[j] > key) j--; if(i < j){ arr[i++] = arr[j]; } while(i < j && arr[i] < key) i++; if(i < j){ arr[j--] = arr[i]; } //arr[i] = key; } arr[i] = key; *pos = i; } int topK(int arr[], int low, int high, int k){ int pos =0; partition(arr, low, high, &pos); int num = high - pos + 1; int index = -1; if(num == k){ index = pos; }else if(num > k){ index = topK(arr, pos + 1, high, k); }else{ index = topK(arr, low, pos -1, k - num); } return index; } int main(){ int arr[10] = {0}; getRand(arr, 10); int pos; pos = topK(arr, 0, 9, 5); for(;pos < 10;pos++){ printf("%d\t", arr[pos]); } printf("\n"); }
这种算法的缺点是需要针对数据做频繁的移动操作,如果数据量较大就需要使用文件的形式进行分治计算。
二、使用最小堆取得TOP K元素是一种很好的方式
至于为为什么使用最小堆,那是因为最小堆堆顶是最小的元素,只有大于堆顶的元素才会加入堆,然后对堆进行adjust。这种方式只需要维护K个元素的 最小堆。
#include <stdio.h> #include <stdlib.h> #include <unistd.h> void print(int arr[], int size){ int i = 0; for(; i < size;i++){ printf("%d\t", arr[i]); } } //最小堆是排序的 void add(int arr[],int size,int num){ if(num > arr[0]){ arr[0] = num; //调整堆 int i = 0; for(;i < size -1 && num > arr[i+1];i++){ arr[i] = arr[i+1]; } arr[i] = num; } } //最小堆不排序(开始元素是1) void sink(int arr[], int size, int num){ if(num > arr[1]){ arr[1] = num; int i = 1; while(2 * i <= size){ int j = 2 * i; if(j < size && arr[j + 1] < arr[j]) j++; if(arr[i] > arr[j]){ int tmp = arr[i]; arr[i] = arr[j]; arr[j] = tmp; i = j; }else{ break; } } } } void getRand(int* data, int length) { int i; srand((unsigned int) getpid()); for (i = 0; i < length; i++) { data[i] = rand() % 1000; } //print the array printf("generate the array using rand:\n"); for (i = 0; i < length; i++) { printf("%d ", data[i]); } printf("\n"); } int main(int argc, char *argv[]){ int arr[11] = {0}; int heap[10] = {0}; int heap2[11] = {0}; getRand(arr, 11); int i; for(;i< 11;i++){ add(heap, 10, arr[i]); sink(heap2, 10, arr[i]); } print(heap, 10); int j = 1; for(; j < 11;j++){ printf("%d\t", heap2[j]); } }