当前位置:   article > 正文

【数据结构】堆(Heap)的概念、结构及其实现_数据结构heap

数据结构heap

目录

一、堆

1.1 堆的概念

1.2 堆的结构性质 

1.3 堆的实现

1.3.1 向上调整

1.3.2 向下调整

1.3.3 创建堆:

1.3.4 插入元素:

1.3.5 删除元素:

1.3.6 堆排序:

1.4 堆的应用

1.4.1 Top K 问题


一、堆

        在C语言中,堆(Heap)是一种基于二叉树结构的数据结构。与内存中的堆不同,此处的堆是指堆数据结构。

1.1 堆的概念

    堆是一种特殊的二叉树,它满足两个性质

  1. 堆总是一棵完全二叉树:除了最后一层外,其他层的节点数都是满的,最后一层的节点都尽量靠左排列。
  2. 堆中每个节点的值都满足堆的性质:堆中某个节点的值总是不大于或不小于其父节点的值。最大堆又称大根堆(Max Heap),父节点的值大于等于其子节点的值;最小堆又称小根堆(Min Heap),父节点的值小于等于其子节点的值。

1.2 堆的结构性质 

        堆是一种基于二叉树结构的数据结构,一般使用数组来实现,因为对于完全二叉树来说,可以利用数组的索引来表示节点之间的关系。

通常,堆具有以下性质

  1. 根节点索引一般为0
  2. 对于堆中其余的任意节点i,它的左孩子节点为2i+1,右孩子节点为2i+2,父节点为(i-1)/2。
  3. 每个节点的左右子树也必须是一个堆
  4. 小堆不是升序,大堆不是降序

1.3 堆的实现

1.3.1 向上调整

        向上调整通常在插入元素到堆中后使用,以确保新插入的元素满足堆的性质。

具体步骤如下

  1. 将新插入的元素放置在堆的最后一个位置(通常是数组的末尾)。
  2. 将该元素与其父节点进行比较。
  3. 若该元素大于(或小于,具体根据堆是最大堆还是最小堆而定)其父节点的值,则交换该元素和其父节点的位置。
  4. 继续向上对比和交换,直到满足堆的性质或达到堆的根节点。
  1. void AdjustUp(HPDataType* arr, int child)
  2. {
  3. assert(arr);
  4. int parent = (child - 1) / 2;
  5. while (child>0)
  6. {
  7. if (arr[child] < arr[parent])
  8. {
  9. swap(&arr[child], &arr[parent]);
  10. child = parent;
  11. parent = (child - 1) / 2;
  12. }
  13. else
  14. {
  15. break;
  16. }
  17. }
  18. }

1.3.2 向下调整

        给出一个数组,逻辑上看做一颗完全二叉树。我们通过从根节点开始的向下调整算法可以把它调整成一个小堆(或大堆)。向下调整算法有一个前提:左右子树必须是一个堆,才能调整。

 具体步骤如下:

  1. 从堆顶元素开始,将其与其子节点中较大(或较小)的节点进行比较。
  2. 若堆顶元素小于(或大于,具体根据堆是最大堆还是最小堆而定)其子节点的值,则将其与较大(或较小)的子节点进行交换,并继续向下比较和交换。
  3. 继续向下对比和交换,直到满足堆的性质或达到堆的末尾。
  1. void AdjustDown(HPDataType* arr,int size, int parent)
  2. {
  3. assert(arr);
  4. HPDataType child = parent * 2 + 1;
  5. while (child < size)
  6. {
  7. if (child + 1 < size && arr[child + 1] < arr[child])
  8. {
  9. child++;
  10. }
  11. if (arr[child] < arr[parent])
  12. {
  13. swap(&arr[parent], &arr[child]);
  14. parent = child;
  15. child = parent * 2 + 1;
  16. }
  17. else
  18. {
  19. break;
  20. }
  21. }
  22. }

1.3.3 创建堆:

 步骤如下:

  1. 创建一个空堆,可以使用一个数组来存储堆的节点,并初始化堆的大小为0。
  2. 一般使用自底向上的方式构建堆,从最后一个非叶子节点开始,依次向上进行堆调整操作。(也可以使用依次插入元素建堆)
  1. //小堆
  2. typedef int HPDataType;
  3. typedef struct {
  4. HPDataType* a;
  5. int size;
  6. int capacity;
  7. }hp;
  8. //初始化堆
  9. void HpInit(hp* php)
  10. {
  11. assert(php);
  12. php->a = NULL;
  13. php->capacity = 0;
  14. php->size = 0;
  15. }
  16. //创建堆
  17. void HPCrearK(HPDataType* arr, int len)
  18. {
  19. assert(arr);
  20. int parent = ((len - 1) - 1) / 2;
  21. while (parent >= 0)
  22. {
  23. AdjustDown(arr, len, parent);
  24. parent--;
  25. }
  26. }

1.3.4 插入元素:

基本思想:

        将一个新的元素插入到堆中,保持堆的性质。

步骤如下:

  1. 将新的元素插入到堆的末尾
  2. 依次与父节点比较,进行交换操作(向上调整),
  3. 重复2,直到满足堆的性质。
  1. //插入元素
  2. void HpPush(hp* php, HPDataType x)
  3. {
  4. assert(php);
  5. if (php->size == php->capacity)
  6. {
  7. int NewCapacity = php->capacity == 0 ? 4 : php->capacity * 2;
  8. HPDataType* tmp = (HPDataType*)realloc(php->a,sizeof(HPDataType) * NewCapacity);
  9. if (tmp == NULL)
  10. {
  11. perror("malloc error!");
  12. return;
  13. }
  14. php->a = tmp;
  15. }
  16. php->a[php->size] = x;
  17. php->size++;
  18. AdjustUp(php->a, php->size-1);
  19. }

1.3.5 删除元素

        删除元素时,通常删除堆顶元素。将堆顶元素与数组末尾的元素交换,然后删除末尾元素,并调整堆的结构,确保堆继续满足堆的性质。

  1. //判空
  2. bool HpEmpty(hp* php)
  3. {
  4. assert(php);
  5. return php->size == 0;
  6. }
  7. //交换元素
  8. void swap(HPDataType* a1, HPDataType* a2)
  9. {
  10. HPDataType tmp = *a1;
  11. *a1 = *a2;
  12. *a2 = tmp;
  13. }
  14. //删除堆顶元素
  15. void HpPop(hp* php)
  16. {
  17. assert(php);
  18. assert(!HpEmpty(php));
  19. swap(&php->a[0] , &php->a[php->size - 1]);
  20. php->size--;
  21. AdjustDown(php->a, php->size, 0);
  22. }

1.3.6 堆排序

        利用堆进行排序的方法称为堆排序。

具体步骤:

  1. 将待排序的数组构建成一个堆(向下调整)
  2. 从后向前依次堆顶元素放入数组中,并删除堆顶元素
  3. 重复2,直至最终得到排序好的数组。
  1. //堆排序
  2. void HpSort(HPDataType* arr, int len)
  3. {
  4. assert(arr);
  5. int parent = ((len - 1) - 1) / 2;
  6. while (parent >= 0)
  7. {
  8. AdjustDown(arr, len, parent);
  9. parent--;
  10. }
  11. int end = len - 1;
  12. while (end > 0)
  13. {
  14. swap(&arr[0], &arr[end]);
  15. // 再调整,选出次小的数
  16. AdjustDown(arr, end, 0);
  17. end--;
  18. }
  19. }

1.4 堆的应用

1.4.1 Top K 问题

TOP-K问题:即求数据结合中前K个最大的元素或者最小的元素,一般情况下数据量都比较大。

具体步骤如下:

  1. 用数据集合中前K个元素来建堆
    前k个最大的元素,建小堆
    前k个最小的元素,则建大堆
  2. 用剩余的N-K个元素依次与堆顶元素来比较,不满足则替换堆顶元素

将剩余N-K个元素依次与堆顶元素比完之后,堆中剩余的K个元素就是所求的前K个最小或者最大的元素。

  1. //创建大小为K的堆
  2. hp* HPCrearK(int k)
  3. {
  4. hp* tmp = (hp*)malloc(sizeof(hp));
  5. if (tmp == NULL)
  6. {
  7. perror("malloc error!");
  8. return NULL;
  9. }
  10. HpInit(tmp);
  11. HPDataType* arr = (HPDataType*)malloc(sizeof(HPDataType) * k);
  12. if (arr == NULL)
  13. {
  14. perror("malloc error!");
  15. return NULL;
  16. }
  17. tmp->a = arr;
  18. tmp->capacity = k;
  19. return tmp;
  20. }
  21. //Topk
  22. HPDataType TopK(HPDataType* arr, int k, int len)
  23. {
  24. assert(arr);
  25. if (k > len)
  26. {
  27. return EOF;
  28. }
  29. hp* php = HPCrearK(k);
  30. if (php == NULL)
  31. {
  32. perror("Creat error");
  33. return EOF;
  34. }
  35. for (int i = 0; i < k; i++)
  36. {
  37. HpPush(php, arr[i]);
  38. }
  39. for (int i = k; i < len; i++)
  40. {
  41. if (arr[i] > HpTop(php))
  42. {
  43. HpPop(php);
  44. HpPush(php, arr[i]);
  45. }
  46. }
  47. return HpTop(php);
  48. }

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

闽ICP备14008679号