当前位置:   article > 正文

<数据结构----二叉树的顺序结构(C语言版)>_二叉树的顺序存储c语言

二叉树的顺序存储c语言

本文目录

前言

1. 二叉树的顺序结构

2. 堆的概念及实现

3. 堆的实现

3.1 堆向下调整算法

3.2 堆的创建

3.3 建堆时间复杂度

3.4 堆的插入

3.5 堆的删除

4. 堆的代码实现(小堆)

堆的结构定义

堆的初始化

堆的销毁

堆的打印

交换两元素值的函数

向上调整算法

向下调整算法

在堆中插入数据

删除堆顶元素

堆的判空

求堆的数据个数

取堆顶的数据

5. 堆的应用

堆排序

堆排序代码实现

TOP-K问题

TOP-K问题代码实现

6. 堆的使用

7. 完整源码

Heap.h

Heap.c

test.c

最终实现效果


前言

本文章主要讲解二叉树的顺序结构及实现方式,想了解树的基本概念的,请移步数据结构----树

1. 二叉树的顺序结构

普通的二叉树是不适合用数组来存储的,因为可能会存在大量的空间浪费。而完全二叉树更适合使用顺序结构存储。现实中我们通常把堆 ( 一种二叉树 ) 使用顺序结构的数组来存储,需要注意的是这里的堆和操作系统 虚拟进程地址空间中的堆是两回事,一个是数据结构,一个是操作系统中管理内存的一块区域分段。

2. 堆的概念及实现

如果有一个关键码的集合 K = {k0,k1,k2,... ,kn-1}, 把它的所有元素按完全二叉树的顺序存储方式存储在一个一维数组中,并满足:Ki <= K2*i+1 且 Ki <= K2*i+2(Ki >= K2*i+1 且 Ki >= K2*i+2),i = 0,1,2...,则称为小堆(大堆 ) 。将根节点最大的堆叫做最大堆或大根堆,根节点最小的堆叫做最小堆或小根堆。
堆的性质:
  • 堆中某个节点的值总是不大于或不小于其父节点的值;
  • 堆总是一棵完全二叉树。

3. 堆的实现

3.1 堆向下调整算法

现在我们给出一个数组,逻辑上看做一颗完全二叉树。我们通过从根节点开始的向下调整算法可以把它调整成一个小堆。向下调整算法有一个前提:左右子树必须是一个堆,才能调整。
int array[] = {27,15,19,18,28,34,65,49,25,37};

3.2 堆的创建

下面我们给出一个数组,这个数组逻辑上可以看做一颗完全二叉树,但是还不是一个堆,现在我们通过算法,把它构建成一个堆。根节点左右子树不是堆,我们怎么调整呢?这里我们从倒数的第一个非叶子节点的子树开始调整,一直调整到根节点的树,就可以调整成堆。
int a[] = {1,5,3,8,7,6};

3.3 建堆时间复杂度

因为堆是完全二叉树,而满二叉树也是完全二叉树,此处为了简化使用满二叉树来证明 ( 时间复杂度本来看的就是近似值,多几个节点不影响最终结果)

因此: 建堆的时间复杂度为 O(N)

3.4 堆的插入

先插入一个 10 到数组的尾上,再进行向上调整算法,直到满足堆。

3.5 堆的删除

删除堆是删除堆顶的数据,将堆顶的数据根最后一个数据一换,然后删除数组最后一个数据,再进行向下调整算法。

4. 堆的代码实现(小堆)

在了解堆的基本实现逻辑后,就可以试着实现整个代码了

堆的结构定义

  1. typedef int HPDataType;
  2. typedef struct Heap
  3. {
  4. HPDataType* a;
  5. size_t size;
  6. size_t capacity;
  7. }HP;

堆的初始化

  1. void HeapInit(HP* php)
  2. {
  3. assert(php);
  4. php->a = NULL;
  5. php->capacity = php->size = 0;
  6. }

堆的销毁

  1. void HeapDestroy(HP* php)
  2. {
  3. assert(php);
  4. free(php->a);
  5. php->a = NULL;
  6. php->capacity = php->size = 0;
  7. }

堆的打印

  1. void HeapPrint(HP* php)
  2. {
  3. assert(php);
  4. for (size_t i = 0; i < php->size; ++i)
  5. {
  6. printf("%d ", php->a[i]);
  7. }
  8. printf("\n");
  9. }

交换两元素值的函数

  1. void Swap(HPDataType* pa, HPDataType* pb)
  2. {
  3. HPDataType tmp = *pa;
  4. *pa = *pb;
  5. *pb = tmp;
  6. }

向上调整算法

  1. void AdjustUp(HPDataType* a, size_t child)
  2. {
  3. size_t parent = (child - 1) / 2;
  4. while (child > 0)
  5. {
  6. if (a[child] < a[parent])
  7. {
  8. Swap(&a[child], &a[parent]);
  9. child = parent;
  10. parent = (child - 1) / 2;
  11. }
  12. else
  13. {
  14. break;
  15. }
  16. }
  17. }

向下调整算法

  1. void AdjustDown(HPDataType* a, size_t size, size_t root)
  2. {
  3. size_t parent = root;
  4. size_t child = parent * 2 + 1;
  5. while (child < size)
  6. {
  7. if (child + 1 < size && a[child] > a[child + 1])
  8. {
  9. child++;
  10. }
  11. if (a[child] < a[parent])
  12. {
  13. Swap(&a[child], &a[parent]);
  14. parent = child;
  15. child = parent * 2 + 1;
  16. }
  17. else
  18. {
  19. break;
  20. }
  21. }
  22. }

在堆中插入数据

  1. void HeapPush(HP* php, HPDataType x)
  2. {
  3. assert(php);
  4. if (php->size == php->capacity)
  5. {
  6. size_t newCapacity = php->capacity == 0 ? 4 : php->capacity * 2;
  7. HPDataType* tmp = realloc(php->a, sizeof(HPDataType) * newCapacity);
  8. if (tmp == NULL)
  9. {
  10. printf("realloc failed\n");
  11. exit(-1);
  12. }
  13. php->a = tmp;
  14. php->capacity = newCapacity;
  15. }
  16. php->a[php->size] = x;
  17. ++php->size;
  18. AdjustUp(php->a, php->size - 1);
  19. }

删除堆顶元素

  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. }

堆的判空

  1. bool HeapEmpty(HP* php)
  2. {
  3. assert(php);
  4. return php->size == 0;
  5. }

求堆的数据个数

  1. size_t HeapSize(HP* php)
  2. {
  3. assert(php);
  4. return php->size;
  5. }

取堆顶的数据

  1. HPDataType HeapTop(HP* php)
  2. {
  3. assert(php);
  4. assert(php->size > 0);
  5. return php->a[0];
  6. }

5. 堆的应用

在我们实现堆后,堆可以用来解决一些实际问题

堆排序

堆排序即利用堆的思想来进行排序,总共分为两个步骤:

 1. 建堆

  • 升序:建大堆
  • 降序:建小堆

 2. 利用堆删除思想来进行排序

建堆和堆删除中都用到了向下调整,因此掌握了向下调整,就可以完成堆排序。

堆排序代码实现

  1. void HeapSort(int* a, int n)
  2. {
  3. // 向上调整建堆 O(N*logN)
  4. /*for (int i = 1; i < n; i++)
  5. {
  6. AdjustUp(a, n);
  7. }*/
  8. // 向下调整建堆 O(N)
  9. for (int i = (n - 1 - 1) / 2; i >= 0; i--)
  10. {
  11. AdjustDown(a, n, i);
  12. }
  13. int j = n;
  14. while (--j)
  15. {
  16. Swap(&a[0], &a[j]);
  17. AdjustDown(a, j, 0);
  18. }
  19. }

TOP-K问题

TOP-K 问题:即求数据结合中前 K 个最大的元素或者最小的元素,一般情况下数据量都比较大
比如:专业前 10 名、世界 500 强、富豪榜、游戏中前 100 的活跃玩家等。
对于 Top-K 问题,能想到的最简单直接的方式就是排序,但是:如果数据量非常大,排序就不太可取了 ( 可能数据都不能一下子全部加载到内存中)
最佳的方式就是用堆来解决,基本思路如下:
1. 用数据集合中前 K 个元素来建堆
  • k个最大的元素,则建小堆
  • k个最小的元素,则建大堆
2. 用剩余的 N-K 个元素依次与堆顶元素来比较,不满足则替换堆顶元素
将剩余 N-K 个元素依次与堆顶元素比完之后,堆中剩余的 K 个元素就是所求的前 K 个最小或者最大的元素。

TOP-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(a, 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. a[5] = 1000000 + 1;
  41. a[1231] = 1000000 + 2;
  42. a[531] = 1000000 + 3;
  43. a[5121] = 1000000 + 4;
  44. a[115] = 1000000 + 5;
  45. a[2335] = 1000000 + 6;
  46. a[9999] = 1000000 + 7;
  47. a[76] = 1000000 + 8;
  48. a[423] = 1000000 + 9;
  49. a[3144] = 1000000 + 10;
  50. PrintTopK(int* a, n, 10);
  51. }

6. 堆的使用

我们来建立一个test函数来测试一下,这需要调用之前我们所定义的相关函数。

  1. void test()
  2. {
  3. HP hp;
  4. HeapInit(&hp);
  5. HeapPush(&hp, 1);
  6. HeapPush(&hp, 5);
  7. HeapPush(&hp, 0);
  8. HeapPush(&hp, 8);
  9. HeapPush(&hp, 3);
  10. HeapPush(&hp, 9);
  11. HeapPrint(&hp);
  12. HeapPop(&hp);
  13. HeapPrint(&hp);
  14. HeapDestroy(&hp);
  15. }

7. 完整源码

Heap.h

  1. #pragma once
  2. // 小堆
  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. // 插入x以后,保持他依旧是(大/小)堆
  19. void HeapPush(HP* php, HPDataType x);
  20. // 删除堆顶的数据。(最小/最大)
  21. void HeapPop(HP* php);
  22. bool HeapEmpty(HP* php);
  23. size_t HeapSize(HP* php);
  24. HPDataType HeapTop(HP* php);

Heap.c

  1. #include "Heap.h"
  2. void Swap(HPDataType* pa, HPDataType* pb)
  3. {
  4. HPDataType tmp = *pa;
  5. *pa = *pb;
  6. *pb = tmp;
  7. }
  8. void HeapInit(HP* php)
  9. {
  10. assert(php);
  11. php->a = NULL;
  12. php->capacity = php->size = 0;
  13. }
  14. void HeapDestroy(HP* php)
  15. {
  16. assert(php);
  17. free(php->a);
  18. php->a = NULL;
  19. php->capacity = php->size = 0;
  20. }
  21. void HeapPrint(HP* php)
  22. {
  23. assert(php);
  24. for (size_t i = 0; i < php->size; ++i)
  25. {
  26. printf("%d ", php->a[i]);
  27. }
  28. printf("\n");
  29. }
  30. void AdjustUp(HPDataType* a, size_t child)
  31. {
  32. size_t parent = (child - 1) / 2;
  33. while (child > 0)
  34. {
  35. if (a[child] < a[parent])
  36. {
  37. Swap(&a[child], &a[parent]);
  38. child = parent;
  39. parent = (child - 1) / 2;
  40. }
  41. else
  42. {
  43. break;
  44. }
  45. }
  46. }
  47. void HeapPush(HP* php, HPDataType x)
  48. {
  49. assert(php);
  50. if (php->size == php->capacity)
  51. {
  52. size_t newCapacity = php->capacity == 0 ? 4 : php->capacity * 2;
  53. HPDataType* tmp = realloc(php->a, sizeof(HPDataType) * newCapacity);
  54. if (tmp == NULL)
  55. {
  56. printf("realloc failed\n");
  57. exit(-1);
  58. }
  59. php->a = tmp;
  60. php->capacity = newCapacity;
  61. }
  62. php->a[php->size] = x;
  63. ++php->size;
  64. AdjustUp(php->a, php->size - 1);
  65. }
  66. void AdjustDown(HPDataType* a, size_t size, size_t root)
  67. {
  68. size_t parent = root;
  69. size_t child = parent * 2 + 1;
  70. while (child < size)
  71. {
  72. if (child + 1 < size && a[child] > a[child + 1])
  73. {
  74. child++;
  75. }
  76. if (a[child] < a[parent])
  77. {
  78. Swap(&a[child], &a[parent]);
  79. parent = child;
  80. child = parent * 2 + 1;
  81. }
  82. else
  83. {
  84. break;
  85. }
  86. }
  87. }
  88. void HeapPop(HP* php)
  89. {
  90. assert(php);
  91. assert(php->size > 0);
  92. Swap(&php->a[0], &php->a[php->size - 1]);
  93. --php->size;
  94. AdjustDown(php->a, php->size, 0);
  95. }
  96. bool HeapEmpty(HP* php)
  97. {
  98. assert(php);
  99. return php->size == 0;
  100. }
  101. size_t HeapSize(HP* php)
  102. {
  103. assert(php);
  104. return php->size;
  105. }
  106. HPDataType HeapTop(HP* php)
  107. {
  108. assert(php);
  109. assert(php->size > 0);
  110. return php->a[0];
  111. }

test.c

  1. #include "Heap.h"
  2. void test()
  3. {
  4. HP hp;
  5. HeapInit(&hp);
  6. HeapPush(&hp, 1);
  7. HeapPush(&hp, 5);
  8. HeapPush(&hp, 0);
  9. HeapPush(&hp, 8);
  10. HeapPush(&hp, 3);
  11. HeapPush(&hp, 9);
  12. HeapPrint(&hp);
  13. HeapPop(&hp);
  14. HeapPrint(&hp);
  15. HeapDestroy(&hp);
  16. }
  17. void HeapSort(int* a, int size)
  18. {
  19. HP hp;
  20. HeapInit(&hp);
  21. for (int i = 0; i < size; ++i)
  22. {
  23. HeapPush(&hp, a[i]);
  24. }
  25. int j = 0;
  26. while (!HeapEmpty(&hp))
  27. {
  28. a[j++] = HeapTop(&hp);
  29. HeapPop(&hp);
  30. }
  31. HeapDestroy(&hp);
  32. }
  33. int main()
  34. {
  35. test();
  36. int a[] = { 4, 2, 7, 8, 5, 1, 0, 6 };
  37. HeapSort(a, sizeof(a) / sizeof(int));
  38. for (int i = 0; i < sizeof(a) / sizeof(int); ++i)
  39. {
  40. printf("%d ", a[i]);
  41. }
  42. printf("\n");
  43. return 0;
  44. }

最终实现效果


后续内容,等博主继续学习后分享给大家。请大家继续关注,不断督促,共同进步!

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

闽ICP备14008679号