• 堆排序详解


    堆:是一种特殊的完全二叉树,一般通过顺序表存储,分为大堆和小堆两类。

    大堆:父节点的值恒大于子节点的值。

    小堆:父节点的值恒小于子节点的值。

    创建堆,可以使得根节点成为整个堆中保存最大或最小的值的结,根据这个特性,堆可以用于排序和解决TopK问题。

    创建堆:

    方法一:向上排序法

    原理:对由前k个结点构成的堆,插入第k+1个结点到最后,然后不断与其父节点进行比对,符合条件就互换,不符合条件就留在原地结束循环。循环结束后,前k+1个结点仍是堆。

    时间复杂度:O(nlogn)

    方法二:向下排序

    原理:首先创造一个完全二叉树,然后从倒数第二层最后一个结点开始,不断以当前结点为根节点创建堆,直至最后以根节点创建堆。每次创建堆,都是先比较该结点的子节点,找出更可能上移的结点,然后与父节点比较,符合条件就互换,然后继续该步骤,不符合条件就停止循环。循环结束后,该部分就成为了一个堆。

    时间复杂度:O(N)

    堆排序:

    思路:将根节点与最后一个结点互换,使得最后一个结点永远保存当前最大(小)的结点,然后对互换后的根节点进行向下排序,使得排完序列后整个二叉树仍然是堆,再将新的根节点与倒数第二个结点互换,重复上述操作。最后,整个堆就是一个升序或降序的顺序表

    时间复杂度:O(NlogN)

    每个根节点最多移动logN次,一共N-1个结点进行移动,因此时间复杂度是NlogN。

    具体代码实现:

    1. void swap1(HPDataType* p1, HPDataType* p2)
    2. {
    3. HPDataType tmp = *p1;
    4. *p1 = *p2;
    5. *p2 = tmp;
    6. }
    7. void up_insert1(HPDataType* a, int i)
    8. {
    9. int child = i;
    10. int parent = (child - 1) / 2;
    11. while (child > 0)
    12. {
    13. if (a[child] < a[parent])
    14. {
    15. swap1(&a[child], &a[parent]);
    16. child = parent;
    17. parent = (child - 1) / 2;
    18. }
    19. else
    20. {
    21. break;
    22. }
    23. }
    24. }
    25. void down_insert1(HPDataType* a, int n, int i)
    26. {
    27. int parent = i;
    28. int child = parent * 2 + 1;
    29. while (child < n)
    30. {
    31. if (child + 1 < n && a[child] > a[child + 1])
    32. {
    33. child++;
    34. }
    35. if (a[parent] > a[child])
    36. {
    37. swap1(&a[parent], &a[child]);
    38. parent = child;
    39. child = child * 2 + 1;
    40. }
    41. else
    42. {
    43. break;
    44. }
    45. }
    46. }
    47. void HeapSort(int* a, int n)
    48. {
    49. //创建堆
    50. for (int i = 1; i < n; i++)
    51. {
    52. up_insert1(a, i);
    53. }
    54. 测试
    55. //for (int i = 0; i < n; i++)
    56. //{
    57. // printf("%d ", a[i]);
    58. //}
    59. //printf("\n");
    60. //排序
    61. for (int i = 0; i < n; i++)
    62. {
    63. swap1(&a[0], &a[n - 1-i]);
    64. down_insert1(a, n - i - 1, 0);
    65. }
    66. //测试
    67. for (int i = 0; i < n; i++)
    68. {
    69. printf("%d ", a[i]);
    70. }
    71. printf("\n");
    72. }

    TopK问题:

    问题要求:对N个数据,只取最大(或最小)的前k个。

    思路:先用前k个数据创建堆,此处要求取最大的前k个,则创建小堆。

    创建小堆后根元素为整个堆中最下的元素,新的元素若比根节点大则替换根节点,然后进行向下排序,使得整个二叉树仍然为堆。进行N-k次上述操作,得到的堆就会保存最大的前k个元素。

    具体代码:

    1. void CreateNDate()
    2. {
    3. // 造数据
    4. int n = 10000;
    5. srand(time(0));
    6. const char* file = "data.txt";
    7. FILE* fin = fopen(file, "w");
    8. if (fin == NULL)
    9. {
    10. perror("fopen error");
    11. return;
    12. }
    13. for (size_t i = 0; i < n; ++i)
    14. {
    15. int x = rand() % 1000000;
    16. fprintf(fin, "%d\n", x);
    17. }
    18. fclose(fin);
    19. }
    20. void swap1(HPDataType* p1, HPDataType* p2)
    21. {
    22. HPDataType tmp = *p1;
    23. *p1 = *p2;
    24. *p2 = tmp;
    25. }
    26. void down_insert1(HPDataType* a, int n, int i)
    27. {
    28. int parent = i;
    29. int child = parent * 2 + 1;
    30. while (child < n)
    31. {
    32. if (child + 1 < n && a[child] > a[child + 1])
    33. {
    34. child++;
    35. }
    36. if (a[parent] > a[child])
    37. {
    38. swap1(&a[parent], &a[child]);
    39. parent = child;
    40. child = child * 2 + 1;
    41. }
    42. else
    43. {
    44. break;
    45. }
    46. }
    47. }
    48. void PrintTopK(int k)
    49. {
    50. const char* file = "data.txt";
    51. FILE* fin = fopen(file, "r");
    52. //创建小堆 (找最大的前k个)
    53. int* arr = (int*)malloc(sizeof(int) * k);
    54. if (arr == NULL)
    55. {
    56. perror("malloc");
    57. exit(-1);
    58. }
    59. int i = 0;
    60. int num = k;
    61. while (num--)
    62. {
    63. fscanf(fin, "%d\n", &arr[i]);
    64. i++;
    65. }
    66. Heap hp;
    67. HeapCreate(&hp, arr, k);
    68. //插入其余元素
    69. int x = 0;
    70. int count = 0;
    71. while (fscanf(fin, "%d\n", &x) != EOF)
    72. {
    73. /* if (count % 1000 == 0)
    74. {
    75. x *= 1000;
    76. }*/
    77. if (x > hp.a[0])
    78. {
    79. hp.a[0] = x;
    80. down_insert1(hp.a, k, 0);
    81. }
    82. }
    83. printf("插入成功\n");
    84. //测试
    85. for (int j = 0; j < k; j++)
    86. {
    87. printf("%d ",hp.a[j]);
    88. }
    89. printf("\n");
    90. }

  • 相关阅读:
    向毕业妥协系列之深度学习笔记(三)DL的实用层面(上)
    d的dip1000,1
    Linux网络编程- ioctl()结合struct ifreq使用案例
    Webfunny 创始人:Skywalking × Zabbix 与观纵探索可观测性
    网页按钮点击动画
    二进制安全虚拟机Protostar靶场 安装,基础知识讲解,破解STACK ZERO
    【C语言】const 关键字
    如果我们真的发现了外星人?
    xgp用什么加速器 xgp加速器免费推荐
    Cadence OrCAD Capture 四种定位到图纸指定位置的方法说明
  • 原文地址:https://blog.csdn.net/yyssas/article/details/132801487