当前位置:   article > 正文

[数据结构]二叉树的顺序存储结构

二叉树的顺序存储结构

目录

二叉树的顺序存储结构::

                                            1.二叉树的顺序结构

                                            2.堆的概念及结构

                                            3.堆的创建

                                            4.建堆时间复杂度的证明

                                            5.堆的插入

                                            6.堆的删除

                                            7.堆的代码实现

                                            8.堆排序

                                            9.Top-K问题

二叉树的顺序存储结构::

二叉树的顺序结构

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

堆的概念及结构:

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

堆的性质:

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

2.堆总是一颗完全二叉树.

 选择题:

  1. 1.下列关键字序列为堆的是:()
  2. A 100,60,70,50,32,65
  3. B 60,70,65,50,32,100
  4. C 65,100,70,32,50,60
  5. D 70,65,100,32,50,60
  6. E 32,50,100,70,65,60
  7. F 50,100,70,65,60,32
  8. 2.已知小根堆为8,15,10,21,34,16,12,删除关键字 8 之后需重建堆,在此过程中,关键字之间的比较次数是()。
  9. A 1
  10. B 2
  11. C 3
  12. D 4
  13. 3.一组记录排序码为(5 11 7 2 3 17),则利用堆排序方法建立的初始堆为
  14. A(11 5 7 2 3 17)
  15. B(11 5 7 2 17 3)
  16. C(17 11 7 2 3 5)
  17. D(17 11 7 5 3 2)
  18. E(17 7 11 3 5 2)
  19. F(17 7 11 3 2 5)
  20. 4.最小堆[0,3,2,5,7,4,6,8],在删除堆顶元素0之后,其结果是()
  21. A[3257468]
  22. B[2357468]
  23. C[2345786]
  24. D[2345678]
  25. 选择题答案:
  26. 1.A
  27. 2.C
  28. 3.C
  29. 4.C

堆的实现:

  堆的创建:

 

  建堆时间复杂度的证明: 

堆的代码实现:
Heap.h

  1. #pragma once
  2. #include<stdio.h>
  3. #include<assert.h>
  4. #include<stdlib.h>
  5. #include<stdbool.h>
  6. #include<time.h>
  7. typedef int HPDataType;
  8. typedef struct Heap
  9. {
  10. HPDataType* a;
  11. int size;
  12. int capacity;
  13. }HP;
  14. void HeapPrint(HP* php);
  15. void AdjustUp(HPDataType* a, int child);
  16. void AdjustDown(HPDataType* a, int n, int parent);
  17. void HeapInit(HP* php);
  18. void Swap(HPDataType* p1, HPDataType* p2);
  19. void HeapDestory(HP* php);
  20. //插入x继续保持堆形态
  21. void HeapPush(HP* php, HPDataType x);
  22. //删除堆顶的元素
  23. void HeapPop(HP* php);
  24. //返回堆顶的元素
  25. HPDataType HeapTop(HP* php);
  26. bool HeapEmpty(HP* php);
  27. int HeapSize(HP* php);

Heap.c

  1. #include"Heap.h"
  2. void HeapInit(HP* php)
  3. {
  4. assert(php);
  5. php->a = NULL;
  6. php->size = php->capacity = 0;
  7. }
  8. void HeapDestory(HP* php)
  9. {
  10. assert(php);
  11. free(php->a);
  12. php->a = NULL;
  13. php->capacity = php->size = 0;
  14. }
  15. void Swap(HPDataType* p1, HPDataType* p2)
  16. {
  17. HPDataType tmp = *p1;
  18. *p1 = *p2;
  19. *p2 = tmp;
  20. }
  21. bool HeapEmpty(HP* php)
  22. {
  23. assert(php);
  24. return php->size == 0;
  25. }
  26. //向上调整算法
  27. //堆的向上调整次数为完全二叉树的层数,即向上调整算法(堆中插入x)的时间复杂度为O(logN)
  28. void AdjustUp(HPDataType* a, int child)
  29. {
  30. int parent = (child - 1) / 2;
  31. //不要用while(parent>=0)作继续条件 当child=0时 parent仍为0 程序陷入死循环
  32. while (child > 0)
  33. {
  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. //插入x继续保持堆形态
  48. void HeapPush(HP* php, HPDataType x)
  49. {
  50. assert(php);
  51. if (php->size == php->capacity)
  52. {
  53. int newCapacity = php->capacity == 0 ? 4 : php->capacity * 2;
  54. HPDataType* tmp = (HPDataType*)realloc(php->a, newCapacity * sizeof(HPDataType));
  55. if (tmp == NULL)
  56. {
  57. perror("realloc fail");
  58. exit(-1);
  59. }
  60. php->a = tmp;
  61. php->capacity = newCapacity;
  62. }
  63. php->a[php->size] = x;
  64. php->size++;
  65. AdjustUp(php->a, php->size - 1);
  66. }
  67. //向下调整算法的前提条件是保证左子树右子树均为堆结构
  68. void AdjustDown(HPDataType* a, int n, int parent)
  69. {
  70. int minChild = parent * 2 + 1;
  71. while (minChild < n)
  72. {
  73. //找出小的那个孩子
  74. if (minChild + 1 < n && a[minChild + 1] < a[minChild])
  75. {
  76. minChild++;
  77. }
  78. if (a[minChild] < a[parent])
  79. {
  80. Swap(&a[minChild], &a[parent]);
  81. parent = minChild;
  82. minChild = parent * 2 + 1;
  83. }
  84. else
  85. {
  86. break;
  87. }
  88. }
  89. }
  90. //删除堆顶的元素——找次大或者次小
  91. //时间复杂度为O(logN)
  92. void HeapPop(HP* php)
  93. {
  94. assert(php);
  95. assert(!HeapEmpty(php));
  96. Swap(&php->a[0], &php->a[php->size - 1]);
  97. php->size--;
  98. AdjustDown(php->a, php->size, 0);
  99. }
  100. //返回堆顶的元素
  101. HPDataType HeapTop(HP* php)
  102. {
  103. assert(php);
  104. assert(!HeapEmpty(php));
  105. return php->a[0];
  106. }
  107. int HeapSize(HP* php)
  108. {
  109. assert(php);
  110. return php->size;
  111. }
  112. void HeapPrint(HP* php)
  113. {
  114. for (int i = 0; i < php->size; i++)
  115. {
  116. printf("%d ", php->a[i]);
  117. }
  118. printf("\n");
  119. }

堆的初始化:

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

堆的销毁:

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

堆的判空:

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

向上调整算法:

  1. //向上调整算法
  2. //堆的向上调整次数为完全二叉树的层数,即向上调整算法(堆中插入x)的时间复杂度为O(logN)
  3. void AdjustUp(HPDataType* a, int child)
  4. {
  5. int parent = (child - 1) / 2;
  6. //不要用while(parent>=0)作继续条件 当child=0时 parent仍为0 程序陷入死循环
  7. while (child > 0)
  8. {
  9. //小于改大于变大堆
  10. if (a[child] < a[parent])
  11. {
  12. Swap(&a[child], &a[parent]);
  13. child = parent;
  14. parent = (child - 1) / 2;
  15. }
  16. else
  17. {
  18. break;
  19. }
  20. }
  21. }

堆的插入:

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

  1. //插入x继续保持堆形态
  2. void HeapPush(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, newCapacity * sizeof(HPDataType));
  9. if (tmp == NULL)
  10. {
  11. perror("realloc fail");
  12. exit(-1);
  13. }
  14. php->a = tmp;
  15. php->capacity = newCapacity;
  16. }
  17. php->a[php->size] = x;
  18. php->size++;
  19. AdjustUp(php->a, php->size - 1);
  20. }

向下调整算法:

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

  1. //向下调整算法的前提条件是保证左子树右子树均为小堆
  2. void AdjustDown(HPDataType* a, int n, int parent)
  3. {
  4. int minChild = parent * 2 + 1;
  5. while (minChild < n)
  6. {
  7. //找出小的那个孩子
  8. if (minChild + 1 < n && a[minChild + 1] < a[minChild])
  9. {
  10. minChild++;
  11. }
  12. if (a[minChild] < a[parent])
  13. {
  14. Swap(&a[minChild], &a[parent]);
  15. parent = minChild;
  16. minChild = parent * 2 + 1;
  17. }
  18. else
  19. {
  20. break;
  21. }
  22. }
  23. }

堆的删除:

  1. //删除堆顶的元素——找次大或者次小
  2. //时间复杂度为O(logN)
  3. void HeapPop(HP* php)
  4. {
  5. assert(php);
  6. assert(!HeapEmpty(php));
  7. Swap(&php->a[0], &php->a[php->size - 1]);
  8. php->size--;
  9. AdjustDown(php->a, php->size, 0);
  10. }

返回堆顶元素:

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

堆的大小:

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

堆的打印:

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

堆的应用:

堆排序建堆: 

 

 

 堆排序代码实现: 

 

  1. void AdjustDown(int* a, int n, int parent)
  2. {
  3. int maxChild = parent * 2 + 1;
  4. while (maxChild < n)
  5. {
  6. //找出小的那个孩子
  7. if (maxChild + 1 < n && a[maxChild + 1] > a[maxChild])
  8. {
  9. maxChild++;
  10. }
  11. if (a[maxChild] > a[parent])
  12. {
  13. Swap(&a[maxChild], &a[parent]);
  14. parent = maxChild;
  15. maxChild = parent * 2 + 1;
  16. }
  17. else
  18. {
  19. break;
  20. }
  21. }
  22. }
  23. void HeapSort(int* a, int n)
  24. {
  25. for (int i = (n - 1 - 1) / 2; i >= 0; i--)
  26. {
  27. AdjustDown(a, n, i);
  28. }
  29. int i = 1;
  30. while (i < n)
  31. {
  32. Swap(&a[0], &a[n - i]);
  33. AdjustDown(a, n - i, 0);
  34. i++;
  35. }
  36. }
  37. #include<stdio.h>
  38. int main()
  39. {
  40. int a[] = { 15,1,19,25,8,34,65,4,27,7 };
  41. HeapSort(a, sizeof(a) / sizeof(a[0]));
  42. for (int i = 0; i < sizeof(a) / sizeof(a[0]); i++)
  43. {
  44. printf("%d ", a[i]);
  45. }
  46. printf("\n");
  47. return 0;
  48. }

Top-K问题:

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

对于Top-K问题,能想到的最简单直接的方式就是排序,但是:如果数据量非常大,排序就不太可取了(可能数据都不能一下子全部加载到内存中)。最佳的方式就是用堆来解决,基本思路如下:

  1. //Top-K问题
  2. void AdjustDown(int* a, int n, int parent)
  3. {
  4. int minChild = parent * 2 + 1;
  5. while (minChild < n)
  6. {
  7. //找出小的那个孩子
  8. if (minChild + 1 < n && a[minChild + 1] < a[minChild])
  9. {
  10. minChild++;
  11. }
  12. if (a[minChild] < a[parent])
  13. {
  14. Swap(&a[minChild], &a[parent]);
  15. parent = minChild;
  16. minChild = parent * 2 + 1;
  17. }
  18. else
  19. {
  20. break;
  21. }
  22. }
  23. }
  24. void CreateDataFile(filename, N)
  25. {
  26. FILE* pf = fopen(filename, "w");
  27. if (pf == NULL)
  28. {
  29. perror("fopen fail");
  30. return;
  31. }
  32. for (int i = 0; i < N; i++)
  33. {
  34. fprintf(pf, "%d ", rand());
  35. }
  36. fclose(pf);
  37. }
  38. void PrintTopK(const char* filename, int k)
  39. {
  40. assert(filename);
  41. FILE* pf = fopen(filename, "r");
  42. if (pf == NULL)
  43. {
  44. perror("fopen fail");
  45. return;
  46. }
  47. int* minHeap = (int*)malloc(sizeof(int) * k);
  48. if (minHeap == NULL)
  49. {
  50. perror("malloc fail");
  51. return;
  52. }
  53. //如何读取前K个数据
  54. for (int i = 0; i < k; i++)
  55. {
  56. fscanf(pf, "%d", &minHeap[i]);
  57. }
  58. //建k个数小堆
  59. for (int j = (k - 1 - 1) / 2; j >= 0; j--)
  60. {
  61. AdjustDown(minHeap, k, j);
  62. }
  63. //继续读取后N-K个
  64. int val = 0;
  65. while (fscanf(pf, "%d", &val) != EOF)
  66. {
  67. if (val > minHeap[0])
  68. {
  69. minHeap[0] = val;
  70. AdjustDown(minHeap, k, 0);
  71. }
  72. }
  73. for (int i = 0; i < k; i++)
  74. {
  75. printf("%d ", minHeap[i]);
  76. }
  77. free(minHeap);
  78. fclose(pf);
  79. }
  80. int main()
  81. {
  82. srand((unsigned int)time(NULL));
  83. const char* filename = "Data.txt";
  84. int N = 10000;
  85. int K = 10;
  86. CreateDataFile(filename, N);
  87. PrintTopK(filename, K);
  88. return 0;
  89. }

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

闽ICP备14008679号