当前位置:   article > 正文

堆排序问题(TOP-K问题)_堆排序一般怎么问

堆排序一般怎么问

目录

1.堆的概念及结构

2.堆的创建

1.框架基本

 2.堆的插入和删除

3.其余函数的实现

3.堆排序问题

1.如何利用数组直接建堆,进行排序

4.TOP-K问题

5.总结



1.堆的概念及结构

如果有一个关键码的集合K ={K0 ,K1,K2,K3…,Kn-1,Kn },把它的所有元素按完全二叉树的顺序存储方式存储 在一个一维数组中,并满足:Ki<k2*i+1 且Ki<K2*i+2 (Ki>k2*i+1 且Ki>K2*i+2)则称为小堆(或大堆)。将根节点最大的堆叫做最大堆或大根堆,根节点最小的堆叫做最小堆或小根堆。

堆的性质:

    堆中某个节点的值总是不大于或不小于其父节点的值;

    堆总是一棵完全二叉树。

例如:

2.堆的创建

1.框架基本

本次代码建立一个小堆(即每一个父亲节点小于孩子节点)

  1. #pragma once
  2. #include<stdio.h>
  3. #include<stdio.h>
  4. #include<assert.h>
  5. #include<stdlib.h>
  6. #include<stdbool.h>
  7. typedef int HPDataType;
  8. typedef struct Heap
  9. {
  10. HPDataType* a;
  11. size_t size;//表示数组的下标
  12. size_t capacity;//表示容量的大小
  13. }HP;
  14. void Swap(HPDataType* pa, HPDataType* pb);//交换两个数据的位置
  15. void HeapInit(HP* php);//堆初始化
  16. void HeapDestroy(HP* php);//堆的销毁
  17. void HeapPrint(HP* php);//打印数组
  18. void HeapPush(HP* php, HPDataType x);//插入数据
  19. void HeapPop(HP* php);//删除数据
  20. bool HeapEmpty(HP* php);//判空
  21. size_t HeapSize(HP* php);//判断堆中元素的个数
  22. HPDataType HeapTop(HP* php);//返回堆中第一个元素

 2.堆的插入和删除

插入

  1. void Swap(HPDataType* pa, HPDataType* pb);
  2. void AdjustUp(HPDataType* a, size_t child);
  3. void AdjustDown(HPDataType* a, size_t size, size_t root);
  4. void HeapPush(HP* php, HPDataType x)
  5. {
  6. assert(php);
  7. if (php->size == php->capacity)
  8. {
  9. size_t newCapacity = php->capacity == 0 ? 4 : php->capacity * 2;
  10. HPDataType* tmp = realloc(php->a, sizeof(HPDataType) * newCapacity);
  11. if (tmp == NULL)
  12. {
  13. printf("realloc failed\n");
  14. exit(-1);
  15. }
  16. php->a = tmp;
  17. php->capacity = newCapacity;
  18. }
  19. php->a[php->size] = x;
  20. ++php->size;
  21. AdjustUp(php->a, php->size - 1);//插入一个元素后,进行向上条整
  22. }
  23. void Swap(HPDataType* pa, HPDataType* pb)
  24. {
  25. HPDataType tmp = *pa;
  26. *pa = *pb;
  27. *pb = tmp;
  28. }
  29. void AdjustUp(HPDataType* a, size_t child)
  30. {
  31. size_t parent = (child - 1) / 2;
  32. while (child > 0)//向上调整,直到根节点
  33. {
  34. if (a[child] < a[parent])//此次建立小堆,若parent节点>child>节点,即交换
  35. {
  36. Swap(&a[child], &a[parent]);
  37. child = parent;
  38. parent = (child - 1) / 2;
  39. }
  40. else
  41. {
  42. break;
  43. }
  44. }
  45. }

删除 

  1. void HeapPop(HP* php)
  2. {
  3. assert(php);
  4. assert(php->size > 0);
  5. Swap(&php->a[0], &php->a[php->size - 1]);//交换第一个节点与最后一个节点
  6. --php->size;//把交换后,处于最后位置的节点删除
  7. AdjustDown(php->a, php->size, 0);//除第一个节点以外,符合堆的性质,采用向下调整的方法
  8. }
  9. void AdjustDown(HPDataType* a, size_t size, size_t root)
  10. {
  11. size_t child= root * 2 + 1;//先找到左孩子节点
  12. size_t parent = root;
  13. while (child<size)
  14. {
  15. if (child + 1 < size && a[child + 1] < a[child])//找到最小的那个孩子节点
  16. {
  17. child++;
  18. }
  19. if (a[parent] > a[child])//如果父亲节点大于最小的那个孩子节点,即交换
  20. {
  21. Swap(&a[parent], &a[child]);
  22. parent = child;
  23. child = parent * 2 + 1;
  24. }
  25. else
  26. {
  27. break;
  28. }
  29. }
  30. }

3.其余函数的实现

  1. void HeapInit(HP* php)
  2. {
  3. php->capacity = php->size = 0;
  4. php->a = NULL;
  5. }
  6. void HeapDestroy(HP* php)
  7. {
  8. php->capacity = php->size = 0;
  9. free(php->a);
  10. php->a = NULL;
  11. }
  12. void HeapPrint(HP* php)
  13. {
  14. assert(php);
  15. for (size_t i = 0; i < php->size; ++i)
  16. {
  17. printf("%d ", php->a[i]);
  18. }
  19. printf("\n");
  20. }
  21. bool HeapEmpty(HP* php)
  22. {
  23. assert(php);
  24. return php->size == 0;
  25. }
  26. size_t HeapSize(HP* php)
  27. {
  28. assert(php);
  29. return php->size;
  30. }
  31. HPDataType HeapTop(HP* php)
  32. {
  33. assert(php);
  34. assert(php->size > 0);
  35. return php->a[0];
  36. }

3.堆排序问题

1.如何利用数组直接建堆,进行排序

  1. int main()
  2. {
  3. int a[] = { 4, 2, 7, 8, 5, 1, 0, 6 };
  4. HeapSort(a, sizeof(a) / sizeof(int));
  5. for (int i = 0; i < sizeof(a) / sizeof(int); ++i)
  6. {
  7. printf("%d ", a[i]);
  8. }
  9. printf("\n");
  10. return 0;
  11. }

1.采用向上调整方法(即上面堆的插入算法)

  1. void HeapSort(int* a, int n)
  2. {
  3. for(int i=1;i<n;i++)//从第2个数据开始向上调整
  4. {
  5. AdjustUp(a,i);
  6. }
  7. while (end > 0)//建堆之后进行排序排序
  8. {
  9. Swap(&a[0], &a[end]);
  10. AdjustDown(a, end, 0);//把第一个元素换到最后,保持其位置不变,再进行向下调整
  11. --end;
  12. }
  13. }

时间复杂度分析 :

节点移动的总次数为:2^1*1+2^2*2+2^3*3+.......+2^(h-1)*h

建堆的时间复杂度为:O(N*logN)

2.采用向下调整算法

向下调整需要确保节点已经是堆,所以可以先从最后面的父亲节点开始,确保其孩子节点与其点满足堆的性质,再往上找父亲节点,再确保其孩子节点与其点满足堆的性质,再进行反复操作,直到根节点。

  1. void HeapSort(int* a, int n)
  2. {
  3. for (int i = (n - 1 - 1) / 2; i >= 0; --i)//建堆
  4. {
  5. AdjustDown(a, n, i);
  6. }
  7. size_t end = n - 1;//最后一个数据的下标
  8. while (end > 0) //建堆之后进行排序排序,此次建的是小堆
  9. {
  10. Swap(&a[0], &a[end]);
  11. AdjustDown(a, end, 0);//把第一个元素(最小的元素)与最后那个元素交换,再进行向下调整
  12. --end; //再把次小的元素与倒数第二个元素交换,(此时最后那个元素不在堆的
  13. //范围内,循环操作)
  14. }
  15. }

时间复杂度分析:

 结论:建堆最好使用向下建堆的方法

4.TOP-K问题

TOP-K问题:即求数据结合中前K个最大的元素或者最小的元素,一般情况下数据量都比较大。 比如:专业前10名、世界500强、富豪榜、游戏中前100的活跃玩家等。

1. 用数据集合中前K个元素来建堆 前k个最大的元素,则建小堆 前k个最小的元素,则建大堆

2. 用剩余的N-K个元素依次与堆顶元素来比较,不满足则替换堆顶元素将剩余N-K个元素依次与堆      顶元素比完之后,堆中剩余的K个元素就是所求的前K个最小或者最大的元素。

  1. void PrintTopK(int* a, int n, int k)
  2. {
  3. // 1. 建堆--用a中前k个元素建堆
  4. int* kminHeap = (int*)malloc(sizeof(int)*k);
  5. assert(kminHeap);
  6. for (int i = 0; i < k; ++i)
  7. {
  8. kminHeap[i] = a[i];
  9. }
  10. // 建小堆
  11. for (int j = (k - 1 - 1) / 2; j >= 0; --j)
  12. {
  13. AdjustDown(kminHeap, k, j);
  14. }
  15. // 2. 将剩余n-k个元素依次与堆顶元素交换,若比堆顶元素大,则进行替换
  16. for (int i = k; i < n; ++i)
  17. {
  18. if (a[i] > kminHeap[0])
  19. {
  20. kminHeap[0] = a[i];
  21. AdjustDown(kminHeap, k, 0);//替换后,在进行向下调整
  22. }
  23. }
  24. for (int j = 0; j < k; ++j)
  25. {
  26. printf("%d ", kminHeap[j]);
  27. }
  28. printf("\n");
  29. free(kminHeap);
  30. }
  31. void TestTopk()
  32. {
  33. int n = 10000;
  34. int* a = (int*)malloc(sizeof(int)*n);
  35. srand(time(0));
  36. for (size_t i = 0; i < n; ++i)
  37. {
  38. a[i] = rand() % 1000000;
  39. }
  40. //随便给出比100000大的数
  41. a[5] = 1000000 + 1;
  42. a[1231] = 1000000 + 2;
  43. a[531] = 1000000 + 3;
  44. a[5121] = 1000000 + 4;
  45. a[115] = 1000000 + 5;
  46. a[2305] = 1000000 + 6;
  47. a[99] = 1000000 + 7;
  48. a[76] = 1000000 + 8;
  49. a[423] = 1000000 + 9;
  50. a[0] = 1000000 + 1000;
  51. PrintTopK(a, n, 10);
  52. }
  53. int main()
  54. {
  55. TestTopk();
  56. return 0;
  57. }
  58. 结果:
  59. 1000001 1000002 1000003 1000005 1000009 1000006 1000004 1000007 1000008 1001000
  60. C:\Users\if\Desktop\world\Project2\Debug\Project2.exe (进程 31404)已退出,代码为 0
  61. 按任意键关闭此窗口. . .

5.总结

要对每一个细节了解清楚,举例进行分析,循序渐进。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/笔触狂放9/article/detail/595951
推荐阅读
相关标签
  

闽ICP备14008679号