• 堆--TopK问题


    题目要求:

    输入整数数组 arr ,找出其中最小的 k 个数。例如,输入4、5、1、6、2、7、3、8这8个数字,则最小的4个数字是1、2、3、4。

    示例 1:

    输入:arr = [3,2,1], k = 2
    输出:[1,2] 或者 [2,1]
    

    示例 2:

    输入:arr = [0,1,2,1], k = 1
    输出:[0]

    思路一:

    先降序排序,排序结束后前10个就是最小的。时间复杂度为O(N*logN)

    一个结点的向下调整时间复杂度只与树的高度有关。时间复杂度为O(logN)

    降序排序最坏情况下需要对每一个结点都进行排序

    思路二:

    将N个数依次插入一个小堆,也就是将N个数字全部保存在一个小堆中。

    PopK次,每次取堆顶的数据删除,并且由于是最小堆,所以每次删除的数字都是堆中最小的数字。

    记录不断选出前K个最小

    将N个数字依次插入大堆中的时间复杂度为O(N);Popk次的时间复杂度为O(k*logN)。所以思路二的时间复杂度为:O(N+k*logN)

    思路二的时间复杂度看起来比思路一的时间复杂度更高,然而实际上一般情况下k远小于N,所以一般情况下思路二时间复杂度更优

    思路三:

    如果N过大,比如N为数亿次,内存中无法存储大量的数据,那么前两种思路也就不可行了

    ①用前k个数建立一个k个数的大堆②剩下的N-k个数,依次跟堆顶的数据进行比较。如果比堆顶的数据小,那么就顶替堆顶数据,在向下调整大堆③最终堆里面的k个数就是最小的k个数

    因为是最大堆,所以堆顶的数据一定是堆中最大的数据,这样就可以保证每次都与堆中最大的对比,从而保留较小值

    时间复杂度:O(K+(N-K)*logK)

    代码实现:

    1. /**
    2. * Note: The returned array must be malloced, assume caller calls free().
    3. */
    4. #pragma
    5. #include
    6. #include
    7. #include
    8. #include
    9. typedef int HPDataType;
    10. typedef struct heap
    11. {
    12. int* a;
    13. int size;
    14. int capacity;
    15. }HP;
    16. void HeapPrintf(HP* hp);//循环输出打印堆中元素
    17. void Swap(HPDataType* px,HPDataType *py);
    18. void AdjustUp(int* a, int size, int child);//向上调整
    19. void AdjustDown(int* a,int size,int parent);//向下调整
    20. void HeapInit(HP* hp);
    21. void HeapDestory(HP* hp);
    22. void HeapPush(HP* hp,HPDataType x);
    23. void HeapPop(HP* hp);//删除堆顶数据
    24. HPDataType HeapTop(HP* hp);//获取堆顶数据
    25. int HeapSize(HP* hp);
    26. bool HeapEmpty(HP* hp);
    27. void Swap(HPDataType* px,HPDataType* py)
    28. {
    29. HPDataType* tmp = *px;
    30. *px = *py;
    31. *py = tmp;
    32. }
    33. void HeapInit(HP* hp)
    34. {
    35. assert(hp);
    36. hp->a = NULL;
    37. hp->size = hp->capacity = 0;
    38. }
    39. void HeapDestory(HP* hp)
    40. {
    41. assert(hp);
    42. free(hp->a);
    43. hp->a = NULL;
    44. hp->size = hp->capacity = 0;
    45. }
    46. //向上调整
    47. //n为数组大小,child为新插入的数据位置
    48. void AdjustUp(int *a,int size,int child)
    49. {
    50. assert(a);
    51. int parent = (child - 1) / 2;
    52. while (child>0)
    53. {
    54. //大堆
    55. if (a[child] > a[parent])
    56. //小堆
    57. //if(a[child]
    58. {
    59. //父子数值交换
    60. Swap(&a[child], &a[parent]);
    61. //更新父子结点
    62. child = parent;
    63. parent = (child - 1) / 2;
    64. }
    65. else
    66. {
    67. break;
    68. }
    69. }
    70. }
    71. //堆插入数据堆其他结点没有影响
    72. //可能会影响它到根节点的路径上的结点关系
    73. void HeapPush(HP* hp, HPDataType x)
    74. {
    75. assert(hp);
    76. if (hp->size==hp->capacity)
    77. {
    78. int newcapacity = hp->capacity == 0 ? 4 : hp->capacity * 2;
    79. HPDataType* tmp = realloc(hp->a, sizeof(HPDataType) * newcapacity);
    80. if (tmp==NULL)
    81. {
    82. printf("realloc fail\n");
    83. exit(-1);
    84. }
    85. hp->a = tmp;
    86. hp->capacity = newcapacity;
    87. }
    88. hp->a[hp->size] = x;
    89. hp->size++;
    90. AdjustUp(hp->a, hp->size, hp->size - 1);
    91. }
    92. void AdjustDown(int *a,int size,int parent)
    93. {
    94. assert(a);
    95. int child = parent * 2 + 1;//左孩子
    96. while (child
    97. {
    98. //选更大的孩子结点,child+1为右孩子
    99. if (child+11]>a[child])
    100. //选更小的孩子节点
    101. //if(child+1
    102. {
    103. child++;//如果右孩子值更大/小,那么child指向右孩子
    104. }
    105. //大堆:如果大的孩子大于父亲则交换
    106. if (a[child]>a[parent])
    107. //小堆,如果小的孩子小于父亲则交换
    108. //if(a[child]
    109. {
    110. Swap(&a[child], &a[parent]);
    111. parent = child;
    112. child = parent * 2 + 1;
    113. }
    114. else
    115. {
    116. break;
    117. }
    118. }
    119. }
    120. void HeapPop(HP* hp)
    121. {
    122. assert(hp);
    123. assert(hp->size > 0);
    124. Swap(&hp->a[0], &hp->a[hp->size - 1]);//将堆顶数据与数组中最后一个数据交换
    125. hp->size--;//删除数组中的最后一个数据
    126. AdjustDown(hp->a, hp->size,0);//传入数组,数组的大小,向下调整起始位置
    127. }
    128. void HeapPrintf(HP* hp)
    129. {
    130. for(int i=0;isize;i++)
    131. {
    132. printf("%d ",hp->a[i]);
    133. }
    134. printf("\n");
    135. }
    136. HPDataType HeapTop(HP* hp)
    137. {
    138. assert(hp);
    139. assert(!HeapEmpty(hp));
    140. return hp->a[0];
    141. }
    142. int HeapSize(HP* hp)
    143. {
    144. return hp->size;
    145. }
    146. bool HeapEmpty(HP* hp)
    147. {
    148. assert(hp);
    149. return hp->size == 0;
    150. }
    151. int* getLeastNumbers(int* arr, int arrSize, int k, int* returnSize)
    152. {
    153. if(k==0||arrSize==0)
    154. {
    155. *returnSize=0;
    156. return NULL;
    157. }
    158. else
    159. {
    160. *returnSize=k;
    161. }
    162. HP hp;
    163. HeapInit(&hp);
    164. for(int i=0;i
    165. {
    166. HeapPush(&hp,arr[i]);
    167. }
    168. for(int i=k;i
    169. {
    170. if(arr[i]<HeapTop(&hp))
    171. {
    172. HeapPop(&hp);
    173. HeapPush(&hp,arr[i]);
    174. }
    175. }
    176. int* ret = (int*)calloc(k, sizeof(int));
    177. for (int i = 0; i < k; i++)
    178. {
    179. ret[i] = HeapTop(&hp);
    180. HeapPop(&hp);
    181. }
    182. return ret;
    183. }

     

     

  • 相关阅读:
    智慧机场航线监测系统:提升航空运输安全与效率的新一步
    安装ESXi 虚拟机
    冒泡排序详细详解
    并行多核体系结构基础 Yan Solihin 第3章 共享存储并行编程 摘录
    算法总结--动态规划
    C 嵌入式系统设计模式 09:硬件适配器模式
    Java Web 7 JavaScript 7.1 JavaScript 简介 && 7.2 JavaScript引入方式
    Kafka 操作之分层存储(Tiered Storage)
    关于开设go语言专题的说明
    5、使用 pgAdmin4 图形化创建和连接 PostgreSQL 数据库
  • 原文地址:https://blog.csdn.net/RXY24601/article/details/127816717