当前位置:   article > 正文

C++ 堆结构和堆排序(从顶到底/从底到顶的大顶堆)+ 优化

C++ 堆结构和堆排序(从顶到底/从底到顶的大顶堆)+ 优化

一、堆结构和堆排序 

(1)heapInsert,向上调整大根堆 和 heapify,向下调整大根堆

  1. // i位置的数,向上调整大根堆
  2. // arr[i] = x,x是新来的!往上看,直到不比父亲大,或者来到0位置(顶)
  3. void heapInsert(vector<int>& arr, int i) {
  4. // i -> 父: (i - 1) / 2
  5. while (arr[i] > arr[(i - 1) / 2]) {
  6. swap(arr, i, (i - 1) / 2);
  7. i = (i - 1) / 2;
  8. }
  9. }
  1. // i位置的数,变小了,又想维持大根堆结构
  2. // 向下调整大根堆
  3. // 当前堆的大小为size
  4. void heapify(vector<int>& arr, int i, int size) {
  5. int l = i * 2 + 1;
  6. while (l < size) {
  7. // 有左孩子,l
  8. // 右孩子,l+1
  9. // 评选,最强的孩子,是哪个下标的孩子
  10. int best = l + 1 < size && arr[l + 1] > arr[l] ? l + 1 : l;
  11. // 上面已经评选了最强的孩子,接下来,当前的数和最强的孩子之前,最强下标是谁
  12. best = arr[best] > arr[i] ? best : i;
  13. // 如果最强的下标,是当前的数,那么当前的数已经满足大根堆结构,退出
  14. if (best == i) { // 最强的是自己
  15. break;
  16. }
  17. swap(arr, best, i);
  18. i = best;
  19. l = i * 2 + 1;
  20. }
  21. }

 二、从顶到底建立大根堆 

 

完整代码:

  1. #include <iostream>
  2. using namespace std;
  3. #include <vector>
  4. // 堆结构和堆排序,填函数练习风格
  5. // 测试链接 : https://leetcode.cn/problems/sort-an-array/
  6. class heapSort {
  7. public:
  8. vector<int> sortArray(vector<int> arr) {
  9. if (arr.size() > 1) {
  10. // heapSort1 为从顶到底建堆然后排序
  11. // heapSort2 为从底到顶建堆然后排序
  12. // 用哪个都可以
  13. heapSort1(arr);
  14. //heapSort2(arr);
  15. }
  16. return arr;
  17. }
  18. // i位置的数,向上调整大根堆
  19. // arr[i] = x,x是新来的!往上看,直到不比父亲大,或者来到0位置(顶)
  20. void heapInsert(vector<int>& arr, int i) {
  21. // i -> 父: (i - 1) / 2
  22. while (arr[i] > arr[(i - 1) / 2]) {
  23. swap(arr, i, (i - 1) / 2);
  24. i = (i - 1) / 2;
  25. }
  26. }
  27. // i位置的数,变小了,又想维持大根堆结构
  28. // 向下调整大根堆
  29. // 当前堆的大小为size
  30. void heapify(vector<int>& arr, int i, int size) {
  31. int l = i * 2 + 1;
  32. while (l < size) {
  33. // 有左孩子,l
  34. // 右孩子,l+1
  35. // 评选,最强的孩子,是哪个下标的孩子
  36. int best = l + 1 < size && arr[l + 1] > arr[l] ? l + 1 : l;
  37. // 上面已经评选了最强的孩子,接下来,当前的数和最强的孩子之前,最强下标是谁
  38. best = arr[best] > arr[i] ? best : i;
  39. // 如果最强的下标,是当前的数,那么当前的数已经满足大根堆结构,退出
  40. if (best == i) { // 最强的是自己
  41. break;
  42. }
  43. swap(arr, best, i);
  44. i = best;
  45. l = i * 2 + 1;
  46. }
  47. }
  48. void swap(vector<int>& arr, int i, int j) {
  49. int tmp = arr[i];
  50. arr[i] = arr[j];
  51. arr[j] = tmp;
  52. }
  53. // 从顶到底建立大根堆,O(n * logn)
  54. // 依次弹出堆内最大值并排好序,O(n * logn)
  55. // 整体时间复杂度O(n * logn)
  56. void heapSort1(vector<int>& arr) {
  57. int n = arr.size();
  58. for (int i = 0; i < n; i++) {
  59. heapInsert(arr, i);
  60. }
  61. int size = n;
  62. while (size > 1) {
  63. swap(arr, 0, --size);
  64. heapify(arr, 0, size);
  65. }
  66. }
  67. };
  68. int main() {
  69. //vector<int> arr = { 10,0,20,5,89,70,65,45 };
  70. //vector<int> arr = { 20,30,15,10,9,8,12,45,0,23 };
  71. vector<int> arr = { 5,6,3,1,9,2,4,6 };
  72. heapSort hs;
  73. vector<int> res = hs.sortArray(arr);
  74. for (int i = 0; i < res.size(); i++) {
  75. cout << res[i] << " ";
  76. }
  77. cout << endl;
  78. return 0;
  79. }

三、从底到顶建立大根堆  

依次弹出堆内最大值并排好序

完整代码:

  1. #include <iostream>
  2. using namespace std;
  3. #include <vector>
  4. // 堆结构和堆排序,填函数练习风格
  5. // 测试链接 : https://leetcode.cn/problems/sort-an-array/
  6. class heapSort {
  7. public:
  8. vector<int> sortArray(vector<int> arr) {
  9. if (arr.size() > 1) {
  10. // heapSort1 为从顶到底建堆然后排序
  11. // heapSort2 为从底到顶建堆然后排序
  12. // 用哪个都可以
  13. //heapSort1(arr);
  14. heapSort2(arr);
  15. }
  16. return arr;
  17. }
  18. // i位置的数,向上调整大根堆
  19. // arr[i] = x,x是新来的!往上看,直到不比父亲大,或者来到0位置(顶)
  20. void heapInsert(vector<int>& arr, int i) {
  21. // i -> 父: (i - 1) / 2
  22. while (arr[i] > arr[(i - 1) / 2]) {
  23. swap(arr, i, (i - 1) / 2);
  24. i = (i - 1) / 2;
  25. }
  26. }
  27. // i位置的数,变小了,又想维持大根堆结构
  28. // 向下调整大根堆
  29. // 当前堆的大小为size
  30. void heapify(vector<int>& arr, int i, int size) {
  31. int l = i * 2 + 1;
  32. while (l < size) {
  33. // 有左孩子,l
  34. // 右孩子,l+1
  35. // 评选,最强的孩子,是哪个下标的孩子
  36. int best = l + 1 < size && arr[l + 1] > arr[l] ? l + 1 : l;
  37. // 上面已经评选了最强的孩子,接下来,当前的数和最强的孩子之前,最强下标是谁
  38. best = arr[best] > arr[i] ? best : i;
  39. // 如果最强的下标,是当前的数,那么当前的数已经满足大根堆结构,退出
  40. if (best == i) { // 最强的是自己
  41. break;
  42. }
  43. swap(arr, best, i);
  44. i = best;
  45. l = i * 2 + 1;
  46. }
  47. }
  48. void swap(vector<int>& arr, int i, int j) {
  49. int tmp = arr[i];
  50. arr[i] = arr[j];
  51. arr[j] = tmp;
  52. }
  53. // 从底到顶建立大根堆,O(n)
  54. // 依次弹出堆内最大值并排好序,O(n * logn)
  55. // 整体时间复杂度O(n * logn)
  56. void heapSort2(vector<int>& arr) {
  57. int n = arr.size();
  58. for (int i = n - 1; i >= 0; i--) {
  59. heapify(arr, i, n);
  60. }
  61. int size = n;
  62. while (size > 1) {
  63. swap(arr, 0, --size);
  64. heapify(arr, 0, size);
  65. }
  66. }
  67. };
  68. int main() {
  69. //vector<int> arr = { 10,0,20,5,89,70,65,45 };
  70. //vector<int> arr = { 20,30,15,10,9,8,12,45,0,23 };
  71. vector<int> arr = { 5,6,3,1,9,2,4,6 };
  72. heapSort hs;
  73. vector<int> res = hs.sortArray(arr);
  74. for (int i = 0; i < res.size(); i++) {
  75. cout << res[i] << " ";
  76. }
  77. cout << endl;
  78. return 0;
  79. }

四、计算复杂度 

总结堆结构

  1. 完全二叉树和数组前缀范围的对应
  2. i的父亲节点:(i-1)/2,i的左孩子:i*2+1,i的左孩子:i*2+2
  3. 堆的定义(大根堆,小根堆),本节课讲解按照大根堆来讲解,小根堆是同理的
  4. 堆的调整:heapInsert(向上调整),heapify(向下调整)
  5. heapInsert、heapify方法的单次调用,时间复杂度O(logn),完全二叉树的结构决定的

堆排序

  • A.从顶到底建堆,时间复杂度O(n*logn),log1 + log2 + log3 + ... + logn -> O(n*logn)
  • B.从底到顶建堆,时间复杂度O(n),总代价就是简单的等比数列关系,为啥会有差异?简单图解一下
  • C.建好堆之后的调整阶段,从最大值到最小值依次归位,时间复杂度O(n*logn),不管以什么方式建堆,调整阶段的时间复杂度都是这个 ,所以整体复杂度也是这个额外空间复杂度是O(1),因为堆直接建立在了要排序的数组上,所以没有什么额外空间

注意:堆结构比堆排序有用的多,尤其是和比较器结合之后,后面博客会重点讲述

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

闽ICP备14008679号