鱼C论坛

 找回密码
 立即注册
查看: 35|回复: 0

[学习笔记] 堆和优先队列

[复制链接]
发表于 昨天 15:49 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能^_^

您需要 登录 才可以下载或查看,没有账号?立即注册

x
本帖最后由 pyzyd 于 2025-10-9 15:49 编辑

堆有最大堆和最小堆,

堆是完全二叉树,
要求:
1.每个父节点大于(或小于)等于其子节点,左右子节点没有大小要求
2.堆最多只能有一个单个的左子节点,不能有多个结点,不能是单个的右子节点
  1.      90
  2.     /  \
  3.    70   80
  4.   / \   / \
  5. 60  20 75 50

  6. 这就是一个最大堆

  7. 错误示例
  8.      1
  9.     / \
  10.    2   3
  11.   /   /
复制代码


堆的代码实现
  1. /*
  2. 堆是特殊的树,
  3. 堆是完全二叉树,
  4. 堆中每一个节点的值都大于或等于其左右孩子节点的值,称为大顶堆(最大堆);
  5. 堆中每一个节点的值都小于或等于其左右孩子节点的值,称为小顶堆(最小堆)。
  6. 堆可以用数组表示,数组中第i个元素,其左孩子为2i+1,右孩子为2i+2。
  7. */

  8. #include <cstring>
  9. #include <iomanip>
  10. #include <iostream>
  11. #include <string>
  12. #include <cmath>

  13. #define DEFAULT_CAPCITY 128

  14. typedef int DataType;

  15. typedef struct _Heap {
  16.     DataType *data;
  17.     int size;
  18.     int capacity;
  19. } Heap;

  20. bool initHeap(Heap &heap, int *original, int size);
  21. bool insert(Heap &heap, DataType value);
  22. static void buildHeap(Heap &heap);
  23. static void adjustDown(Heap &heap, int index);
  24. static void adjustUp(Heap &heap, int index);


  25. int main() {
  26.     Heap hp;
  27.     int origVals[] = {1, 2, 3, 87, 93, 82, 92, 86, 95};
  28.     if (!initHeap(hp, origVals, sizeof(origVals) / sizeof(origVals[0]))) {
  29.         std::cerr << "init heap failed!" << std::endl;
  30.         return -1;
  31.     }

  32.     if (typeid(1.0) == typeid(float)) {
  33.         std::cout << "1.0 is float" << std::endl;
  34.     } else {
  35.         std::cout << "1.0 is double" << std::endl;
  36.     }
  37.     insert(hp, 100);
  38.     return 0;
  39. }


  40. bool initHeap(Heap &heap, int *original, int size) {
  41.     if (original == nullptr || size <= 0) {
  42.         return false;
  43.     }
  44.     heap.capacity = DEFAULT_CAPCITY > size ? DEFAULT_CAPCITY : size;
  45.     heap.data = new DataType[heap.capacity];
  46.     if (!heap.data) {
  47.         return false;
  48.     }
  49.     memcpy(heap.data, original, size * sizeof(int));
  50.     heap.size = size;
  51.     buildHeap(heap);
  52.     return true;
  53. }

  54. bool insert(Heap &heap, DataType value) {
  55.     if (heap.size == heap.capacity) {
  56.         return false;
  57.     }
  58.     int index = heap.size++;
  59.     heap.data[index] = value;
  60.     adjustUp(heap, index);
  61.     return true;
  62. }

  63. /*
  64. 从最后一个非叶子结点(父节点 size / 2 - 1
  65. 的位置)逐个往前调整所有父节点(直到根节点),
  66. 确保每一个父节点都是一个最大堆,最终整个堆就是一个最大堆。
  67. */
  68. static void buildHeap(Heap &heap) {
  69.     int i;
  70.     for (i = heap.size / 2 - 1; i >= 0; i--) {
  71.         adjustDown(heap, i);
  72.     }
  73. }

  74. /*
  75. 将当前节点和子节点调整成最大堆
  76. */
  77. static void adjustDown(Heap &heap, int index) {
  78.     int cur = heap.data[index];
  79.     int parent, child;
  80.     for (parent = index; (parent * 2 + 1) < heap.size; parent = child) {
  81.         child = parent * 2 + 1;
  82.         // 比较左子节点和右子节点,取较大的那个
  83.         if ((child + 1) < heap.size &&
  84.             heap.data[child] < heap.data[child + 1]) {
  85.             child++;
  86.         }
  87.         // 如果当前节点大于等于最大子节点,则不需要调整
  88.         if (cur >= heap.data[child]) {
  89.             break;
  90.         } else {
  91.             // 否则,将当前节点和最大子节点交换
  92.             heap.data[parent] = heap.data[child];
  93.             heap.data[child] = cur;
  94.         }
  95.     }
  96. }

  97. static void adjustUp(Heap &heap, int index) {
  98.     if (index < 0 || index >= heap.size) {
  99.         return;
  100.     }
  101.     while (index > 0) {
  102.         int parent = (index - 1) / 2;
  103.         if (heap.data[index] <= heap.data[parent]) {
  104.             break;
  105.         }

  106.         // 交换当前节点与父节点
  107.         int temp = heap.data[index];
  108.         heap.data[index] = heap.data[parent];
  109.         heap.data[parent] = temp;

  110.         index = parent;
  111.     }
  112. }
复制代码

用堆来实现优先队列,利用了堆可以用数组存储的特性
  1. #include <iostream>
  2. #include <cstring>
  3. #include <random>

  4. #define DEFAULT_CAPACITY 128

  5. typedef struct _task{
  6.     int priority;

  7. }Task;
  8. typedef Task DataType;

  9. typedef struct _PriorityQueue {
  10.     DataType* data;
  11.     int size;
  12.     int capacity;
  13. }PriorityQueue;

  14. bool init(PriorityQueue &pq, DataType *original, int size);
  15. bool push(PriorityQueue &pq, DataType value);
  16. bool pop(PriorityQueue &pq, DataType &value);
  17. bool isEmpty(PriorityQueue &pq);
  18. bool isFull(PriorityQueue &pq);
  19. void destroy(PriorityQueue &pq);

  20. static void build(PriorityQueue &pq);
  21. static void adjustDown(PriorityQueue &pq, int index);
  22. static void adjustUp(PriorityQueue &pq, int index);

  23. bool init(PriorityQueue &pq, DataType *original, int size) {
  24.     if (!original || size <= 0) {
  25.         return false;
  26.     }
  27.     pq.capacity = size > DEFAULT_CAPACITY ? size : DEFAULT_CAPACITY;
  28.     pq.data = new DataType[pq.capacity];
  29.     // // 第一种初始化方式
  30.     // memcpy(pq.data, original, size * sizeof(DataType));
  31.     // pq.size = size;
  32.     // build(pq);

  33.     // 第二种初始化方式
  34.     pq.size = 0;
  35.     for (int i = 0; i < size; i++) {
  36.         push(pq, original[i]);
  37.     }
  38.     return true;
  39. }

  40. static void build(PriorityQueue &pq) {
  41.     for (int i = pq.size / 2 - 1; i >= 0; i--) {
  42.         adjustDown(pq, i);
  43.     }
  44. }

  45. static void adjustDown(PriorityQueue &pq, int index) {
  46.     DataType cur = pq.data[index];
  47.     int parent = index;
  48.     int child;
  49.    //从当前的节点开始,向下调整子节点及子节点的子节点使数组满足堆的特性
  50.     while (parent * 2 + 1 < pq.size) {
  51.         child = parent * 2 + 1;
  52.         if (child + 1 < pq.size && pq.data[child].priority < pq.data[child + 1].priority) {
  53.             child++;
  54.         }
  55.         if (pq.data[child].priority > cur.priority) {
  56.             pq.data[parent] = pq.data[child];  // 子节点上移
  57.             parent = child;
  58.         } else {
  59.             break;
  60.         }
  61.     }
  62.     pq.data[parent] = cur;
  63. }

  64. bool push(PriorityQueue &pq, DataType value) {
  65.     if (isFull(pq)) {
  66.         return false;
  67.     }
  68.     pq.data[pq.size++] = value;
  69.     adjustUp(pq, pq.size - 1);
  70.     return true;
  71. }

  72. bool isFull(PriorityQueue &pq) {
  73.     return pq.size == pq.capacity;
  74. }

  75. bool isEmpty(PriorityQueue &pq) {
  76.     return pq.size == 0;
  77. }

  78. static void adjustUp(PriorityQueue &pq, int index) {
  79.     DataType cur = pq.data[index];
  80.     int child, parent;
  81.     for (child = index; child > 0; child = parent) {
  82.         parent = (child - 1) / 2;
  83.         if (pq.data[parent].priority < cur.priority) {
  84.             pq.data[child] = pq.data[parent];
  85.             pq.data[parent] = cur;
  86.         } else {
  87.             break;
  88.         }
  89.     }
  90. }

  91. bool pop(PriorityQueue &pq, DataType &value) {
  92.     if (isEmpty(pq)) {
  93.         return false;
  94.     }
  95.     value = pq.data[0];
  96.     pq.data[0] = pq.data[--pq.size];
  97.     adjustDown(pq, 0);
  98.     return true;
  99. }

  100. void destroy(PriorityQueue &pq) {
  101.     delete[] pq.data;
  102.     pq.data = nullptr;
  103.     pq.size = pq.capacity = 0;
  104. }

  105. int main()
  106. {
  107.     std::mt19937 gen(time(0)); // random device
  108.     std::uniform_int_distribution<int> dis(0, 9);
  109.     PriorityQueue pq;
  110.     Task tasks[7];
  111.     for (int i = 0; i < 7; i++) {
  112.         tasks[i].priority = dis(gen);
  113.     }

  114.     if (!init(pq, tasks, sizeof(tasks) / sizeof(Task))) {
  115.         std::cerr << "init failed" << std::endl;
  116.         return -1;
  117.     }
  118.     std::cout << "Tasks priority:" <<std::endl;
  119.     for (int i = 0; i < pq.size; i++){
  120.         std::cout << tasks[i].priority << " ";
  121.     }
  122.     std::cout << std::endl;
  123.     std::cout << "Priority queue:" << std::endl;
  124.     for (int i = 0; i < pq.size; i++){
  125.         std::cout << pq.data[i].priority << " ";
  126.     }
  127.     std::cout << std::endl;

  128.     push(pq, (Task){100});
  129.     std::cout << "Priority queue after push:" << std::endl;
  130.     for (int i = 0; i < pq.size; i++){
  131.         std::cout << pq.data[i].priority << " ";
  132.     }
  133.     std::cout << std::endl;
  134.     return 0;
  135. }
复制代码

小甲鱼最新课程 -> https://ilovefishc.com
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2025-10-10 10:56

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表