介绍
top k问题即求前K大/小或求第K大/K小
常用方法
大根堆求前/第 K 小
小根堆求前/第 K 大
快排分割函数求topK问题
小根堆求前K大案例
要求:求vec中前10大的数
小根堆找前top k大的数,就是初始化一个大小为K的堆,用当前遍历值和堆顶比较,堆顶 <当前遍历,出堆顶,将当前遍历插入堆。
int main()
{
vector<int> vec;
for (int i = 0; i < 100000; ++i)
{
vec.push_back(rand() + i);
}
// 小根堆
priority_queue<int, vector<int>, greater<int>> minHeap;
// 1.往小根堆放10个元素
int k = 0;
for (; k < 10; ++k)
{
minHeap.push(vec[k]);
}
// 2.将剩余元素依次和堆顶元素比较,若大于堆顶则出堆顶入新元素
// 最后堆中元素就是前10大的元素,堆顶为第10大的元素
for (; k < vec.size(); ++k)
{
if (vec[k] > minHeap.top())
{
minHeap.pop();
minHeap.push(vec[k]);
}
}
// 输出前10大的数
while (!minHeap.empty())
{
cout << minHeap.top() << " ";
minHeap.pop();
}
cout << endl;
return 0;
}
快排分割求第K小元素
// 快排分割
int partition(vector<int>& arr, int i, int j)
{
int k = arr[i]; // 选取最左元素为排序元素
while (i < j)
{
while (i < j && arr[j] > k) j--;
if (i < j)
{
arr[i++] = arr[j];
}
while (i < j && arr[i] < k) i++;
if (i < j)
{
arr[j++] = arr[i];
}
}
arr[i] = k; // k必然已经到达排序后的位置了第 i+1 小的元素
return i;
}
// i 查找数据的起始下标
// j 查找数据的结束下标
// k 待查找的第k个元素,下标为k-1
int selectNoK(vector<int>& arr, int i, int j, int k)
{
int pos = partition(arr, i, j);
if (pos == k - 1)
{
return pos;
}
else if (pos < k - 1)
{
return selectNoK(arr, pos + 1, j, k);
}
else
{
return selectNoK(arr, i, pos - 1, k);
}
}
int main()
{
// 求vec中第10小的数——使用快排分割方法
vector<int> vec;
for (int i = 0; i < 100000; ++i)
{
vec.push_back(rand() + i);
}
// 内部是对vec有进行排序的,所以返回的pos就是9
int pos = selectNoK(vec, 0, vec.size() - 1, 10);
cout << vec[pos] << endl;
return 0;
}
补充问题
如果有一个大文件,里面放的是整数,内存限制200M,求最大的前10个?
分析:采用分治的思想
计算一下整数文件的大小: 文件大小 / 200M = 要分的小文件数量
哈希映射:整数 % 小文件个数 = file_index
现在每一个小文件就可以加载到内存中,对每个小文件的整数求topK,最后归并