TOP-K :快速选择算法 & 堆解法
1. 从快排partition过程借鉴而来,利用partion过程每次能确定一个元素位置来实现。
此算法期望时间复杂度为O(N),最差为O(N^2)。
每次确定了一个元素位置后,就能判断待查的topk个元素是在哪个分区,只需要递归一个分区即可。
代码如下:
int quickSelect(vector<int>&, int, int, int); int findKthSmallest(vector<int>& arrs, int k) { int sz = arrs.size(); if (k > sz) return -1; return quickSelect(arrs, k, 0, sz - 1); } int quickSelect(vector<int>& arrs, int k, int l, int r) { //选pivot为最右边那个 更好的方式是随机选一个 //int pivot = arrs[r]; int random_idx = rand() % (r-l+1) + l; swap(arrs[random_idx], arrs[r]); //int pivot = arrs[r]; // partition过程 int pivot = arrs[r]; int i = l - 1; for (int j = l; j < r; ++j) { //维护一个<= > ?三个区域 if (arrs[j] <= pivot) { swap(arrs[++i], arrs[j]); } } swap(arrs[i+1], arrs[r]); //i+1为最后中轴所在的位置 //递归过程 if (k == (i+1)) { return arrs[i+1]; } else if (k < (i+1)) { return quickSelect(arrs, k, l, i); } else { return quickSelect(arrs, k, i+2, r); } }
2. 利用堆来实现TOP-K问题
维护一个包含K个元素的堆,求第K大的维护小根堆,求第K小的维护大根堆。
时间复杂度为:O(nlogk)。
代码如下:
//建堆 + 拔尖 + 调整 void adjustHeap(vector<int>& arr, int i, int len); int findKthSmallest(vector<int>& nums, int k) { //建立一个大顶堆 vector<int> arr(k, 0); for (int i = 0; i < k; ++i) { arr[i] = nums[i]; } for (int i = arr.size() / 2; i >= 0; --i) { adjustHeap(arr, i, arr.size()); } for (int i = k; i < nums.size(); ++i) { if (nums[i] >= arr[0]) continue; arr[0] = nums[i]; adjustHeap(arr, 0, arr.size()); } return arr[0]; }
//调整 void adjustHeap(vector<int>& arr, int i, int len) { int tmp = arr[i]; for (int k = 2*i+1; k < len; k = k*2+1) { if (k < len-1 && arr[k] <= arr[k+1]) k++; if (arr[k] > tmp) { arr[i] = arr[k]; i = k; } else break; } arr[i] = tmp; }
以上是常用的两种解法,另外:
复杂度更高的解法有:
- 冒泡,时间复杂度O(KN)或者O((N-K)N),考虑K与n/2的大小;
- 直接用别的排序手段全排序,如归并,O(NlogN)。
复杂度更低的解法:
- BFPRT算法,分5组,每组取中位数,可以每次筛选掉更多的元素,时间复杂度严格的O(N)。
其他的变种,大文件中具有超大规模数,求TOP-K:
- 可以采用分治算法,结合哈希算法来分到不同文件,每个小文件进行TOP-K处理,再进行合并,类似Map-Reduce过程。