• C语言描述数据结构 —— 二叉树(2)Top-K问题


    1.前言

    Top-K问题在生活当中是非常常见的,例如各个板块的排行榜、电商平台的产品热度排名、价格排序……这些例子统统围绕一个核心:从N个数据中找出k个最大、最小、最热等的数据。我们可以使用堆来解决Top-K问题,即求数据中前k个最大或最小的元素,当然处理的数据量也非常庞大。其实在处理一般数据的时候,我们想到的一般方法是对数据进行排序,排序就意味着数据是有序的,有序的就可以很简单的选出前k个符合条件的数,但是数据量一旦大了起来,排序就显得不合适了。

    2.使用堆解决Top-K问题

    当我们要使用堆来处理数据的时候,就必然会涉及建堆的问题,那么建堆的核心点就在于,我们要建一个多大的堆,是大堆还是小堆。我们在实现堆这个数据结构的时候,实现了一个HeapPop接口,删除堆中堆顶的元素,删除的逻辑相信大家也能够理解,即可以找到次大或次小的数,但是这样做是得不偿失的,首先如果数据量很大,那么空间就是一个问题,因为我们要建一个非常大的堆,其次就是每次删除之后要重新建堆,那么时间复杂度就上去了,所以这并不是一个好的解决方案。 

    总上所述,我们得出结论:

    • 找前k个最小的数,建小堆不可取
    • 找钱k个最大的数,建大堆不可取

    如果我们反其道而行之,找前k个最小的数,建大堆可以吗?找前k个最大的数,建大堆可以吗?我们介绍一种算法。

    可以看到,我们的空间复杂度、时间复杂度都降下去了,而且算法效率也非常的高。只不过需要注意一点,最后的结果不一定是有序的,但它一定是符合条件的。 所以我们得出结论:

    • 找前k个最大的数据,建小堆
    • 找前k个最小的数据,建大堆

    现在就可以将思路转换成代码了,不过在这里呢,我们使用文件的方式来存储数据,这就在一定程度上模拟了很多排序算法

    1. #include
    2. #include
    3. #include
    4. //文件中写入随机数
    5. void CreateData(const char* filename, int N)
    6. {
    7. assert(filename);
    8. FILE* fin = fopen(filename, "w");
    9. assert(fin);
    10. srand((unsigned int)time(NULL));
    11. for (int i = 0; i < N; i++)
    12. {
    13. fprintf(fin, "%d\n", rand());
    14. }
    15. fclose(fin);
    16. }
    17. //交换
    18. void Swap(int* p1, int* p2)
    19. {
    20. assert(p1 && p2);
    21. int tmp = *p1;
    22. *p1 = *p2;
    23. *p2 = tmp;
    24. }
    25. //向下调整
    26. void AdjustDown(int* ph, int n, int parent)
    27. {
    28. assert(ph);
    29. int minchild = parent * 2 + 1;
    30. while (minchild < n)
    31. {
    32. if (minchild + 1 < n && ph[minchild + 1] < ph[minchild])
    33. {
    34. minchild++;
    35. }
    36. if (ph[minchild] < ph[parent])
    37. {
    38. Swap(&ph[minchild], &ph[parent]);
    39. parent = minchild;
    40. minchild = parent * 2 + 1;
    41. }
    42. else
    43. {
    44. break;
    45. }
    46. }
    47. }
    48. void PrintTopK(const char* filename, int K)
    49. {
    50. assert(filename);
    51. //建立文件
    52. FILE* fout = fopen(filename, "r");
    53. assert(fout);
    54. //把前k个元素放入堆中
    55. int* minHeap = (int*)malloc(sizeof(int) * K);
    56. assert(minHeap);
    57. for (int i = 0; i < K; i++)
    58. {
    59. fscanf(fout, "%d", &minHeap[i]);
    60. }
    61. //找前k个最大的数据,建小堆
    62. for (int i = (K-2)/2; i>=0; i--)
    63. {
    64. AdjustDown(minHeap, K, i);
    65. }
    66. //堆顶元素与剩下的元素比较
    67. int val = 0;
    68. while (fscanf(fout, "%d", &val) != EOF)
    69. {
    70. if (val > minHeap[0])
    71. {
    72. minHeap[0] = val;
    73. AdjustDown(minHeap, K, 0);
    74. }
    75. }
    76. //打印
    77. for (int i = 0; i < K; i++)
    78. {
    79. printf("%d ", minHeap[i]);
    80. }
    81. //关闭文件
    82. fclose(fout);
    83. }
    84. int main()
    85. {
    86. int N = 10000;
    87. int K = 10;
    88. const char* filename = "Data.txt";
    89. //CreateData(filename, N);
    90. PrintTopK(filename,K);
    91. return 0;
    92. }

    如果我们对打印的结果有问题的话,我们还可以去文件中随机加几个值:

     如果大家想完成前k个最小的数,可以尝试建大堆,并改变一下堆顶元素的比较的条件。

  • 相关阅读:
    【媒体邀约】媒体宣传——企业成长的催化剂
    过来人:玩游戏不如做游戏,会上瘾的建模工作
    mysql面试题32:MySQL数据库服务器性能分析的方法命令有哪些?
    207.课程表 | 210.课程表II
    .Net C# 发送带背景图html邮件(解决Outlook不显示背景图问题)
    Spark Driver CPU 占用异常问题排查
    聊聊 Netty 那些事儿之 Reactor 在 Netty 中的实现(创建篇)
    ESP8266开发环境搭建踩坑
    defcon-quals 2023 crackme.tscript.dso wp
    你已经应用了哪种服务注册和发现的模式呢?
  • 原文地址:https://blog.csdn.net/weixin_59913110/article/details/126482056