目录:
topk问题
从N个数中找出最大(最小)的前k个
现实中的场景如:王者荣耀英雄排名(长安区第一百韩信),外面商家店铺排名(长安区冒菜销量第二)
1.思路一:排序(堆排序)
时间复杂度为:O(N*log2N)
如我们要找出最大的前k个数,我们可以对这N个数进行一个降序排序,然后输出前k个数即可;降序我们就要建小堆,然后将根节点和最后一个叶子节点交换,不把最后一个数看作堆里的数据,然后在进行向下调整;具体分析参考堆排序
1.1 代码实现
//降序
void HeapSort(int* a, int n)
{
//建小堆
for (int i = (n - 1 - 1) / 2; i >= 0; --i)
{
AdjustDown(a, n, i);
}
int end = n - 1;
//把最小的换到最后一个位置,不把最后一个数看作堆里的
//每次选出剩下数中最小的
//从后往前放
while (end > 0)
{
int tem = a[end];
a[end] = a[0];
a[0] = tem;
AdjustDown(a, end, 0);
--end;
}
}
1.2测试
int main()
{
int arr[] = {
27, 28, 65, 25, 15, 34, 19, 49, 18, 37 };
int n = sizeof(arr) / sizeof(int);
printf("全部数据:");
for (int i = 0; i < n; i++)
{
printf("%d ", arr[i]);
}
printf("\n");
HeapSort(arr, n);
printf("最大的前5个数:");
for (int j = 0;j < 5; j++)
{
printf("%d ", arr[j]);
}
printf("\n");
return 0;
}
2.思路二:建大堆,,删掉最大的,调堆继续选,直到选出k个次大的
时间复杂度为:O(N+k*log2N)
当N非常大时此思路的时间复杂度趋近于O(N)
由于思路一效率不高,优化后得到思路二------->如果我们要找出最大的前k个数,我们可以建一个大堆,然后将根节点(最大值)删除,然后使用向下调整算法选出次大的,再删除,直到选出k个次大的数
此思路需要建一次堆时间复杂度为O(N),进行k次向下调整算法时间复杂度为O(k*log2N),所以此思路的总时间复杂度为O(N+klog2N),当N非常大时log2N非常平缓,而O(N)趋近于无穷大,所以当N非常大时此思路的时间复杂度趋近于O(N)
3.思路三–>最优解(省空间,效率相对较高):
时间复杂度为:O(k+(N-k)*log2k)
当N非常大时此思路的时间复杂度趋近于O(N)
若N非常大内存中放不下N个数思路二就不可行,优化得到思路三------->如果我们要找出最大的前k个数,建K个数大小的小堆,剩下的N-K个数依次去跟堆顶的数据比较,如果比堆顶的数据大,就替换(直接覆盖)堆顶的数据,再向下调整,如此循环最后最大的前K个数就在堆里
此思路建k个数大小的堆,时间复杂度为O(k),需要进行N-K次向下调整所以时间复杂度为O((N-k)*log2k),所以思路三总时间复杂度为O(k+(N-k)*log2k),实际应用中N是非常大的,而k相比于N就是一个非常小的值,所以当N非常大时思路三的时间复杂度也趋近于O(N)
3.1代码实现
void TopK(int* a,int k,int n)
{
//开辟k个大小的空间
int* heapA = (int*)malloc(k * sizeof(int));
for (int i = 0; i < k; ++i)
{
heapA[i] = a[i];
}
//建k个数大小的小堆
for (int i = (k - 2) / 2; i >= 0; --i)
{
AdjustDown(heapA, k, i);
}
//从第K个位置开始和heapA[0]比较,大就覆盖heapA[0],
//然后继续调整,heapA[0]始终是堆里最小的数
for (int i = k; i < n; i++)
{
if (a[i] > heapA[0])
{
heapA[0] = a[i];
AdjustDown(heapA, k, 0);
}
}
//打印
for (int i = 0; i < k; i++)
{
printf("%d ", heapA[i]);
}
printf("\n");
free(heapA);
}
3.2测试
int main()
{
int arr[] = {
27, 28, 65, 25, 15, 34, 19, 49, 18, 37 };
int n = sizeof(arr) / sizeof(int);
printf("全部数据:");
for (int i = 0; i < n; i++)
{
printf("%d ", arr[i]);
}
printf("\n");
TopK(arr, 5, n);
return 0;
}