当前位置:   article > 正文

C语言二叉树深度详解(附带源码)_二叉树的生成遍历和求深度 c语言代码

二叉树的生成遍历和求深度 c语言代码

目录

一、基本概念:

二、实现二叉树的数据结构:

三、二叉树性质:

四、相关计算

五、搜索二叉树:任何一颗树的左子树都比它小,右子树都比它大

六、二叉树的存储结构

七、二叉树基本操作

八、源码(有需要的同学,可以直接拿去交作业了)

头文件:

实现函数文件:

          测试文件:

运行截图:


一、基本概念:


:一个节点含有的子树的个数称为该节点的度
叶节点或者终端节点:度为0的节点
非终端节或和分支节点:度不为0的节点 
双亲结点或父节点:一个节点含有子节点,这个节点称为子节点的父节点(国外有些书称为双亲结点,涉及到女权主义)
孩子节点或子节点:一个节点含有的子树的跟节点称为该节点的子节点
兄弟节点(亲兄弟):还有同一个父节点的节点互称为兄弟节点
树的度:一棵树中,最大的节点的度称为数的度
树的层数、高度、深度:从根节点开始,根为第一层,根的子节点为第二层,以此类推
(关于空树的高度最好从1开始,如果从0开始,空二叉树高度为0,而你只有一个根节点也称为0,矛盾)
节点的祖先:从该节点开始,往上回溯,都是该节点的祖先
子孙:该节点以下的节点都是其子孙
森林:有n棵不相交的树的集合叫做森林

二叉树就是度不超过2的树

树在实际中的应用:文件系统的目录就是一个树结构


二、实现二叉树的数据结构:


1、左孩子右兄弟法:用链表实现,每个节点有一个左孩子和一个右兄弟。每个节点只有一个左孩子但是可以有多个右兄弟


2、双亲表示法:用数组实现,每一个节点只记录其父亲


二叉树的遍历一般采用分治算法:分而治之,将大问题分成类似的子问题,直到子问题不可再分割

因为任何一颗树可以分成三个部分:
1、根节点
2、左子树
3、右子树

二叉树的四种遍历策略:
1、先序(先根):根 左子树 右子树 。先根,再左子树,此时的左子树又可以看成以该左节点为根的左子树,再循环遍历;
直到访问到最后一个左子树,即左叶节点,此时该节点的左子树为NULL,再到右子树也为NULL,此时,该节点算访问完成,即左子树已经遍历完毕,
左子树既然遍历完了,那就自然到了右子树,如此循环


2、中序(中根):左子树  根 右子树//也就是说实际上最小的问题是只剩下最后一个节点,再继续访问其左子树和右子树,但是为空,可以执行,不要忽略访问NULL,便于理解


3、后序(后根):左子树 右子树 根

先序、中序、后序是相对于左子树、右子树、根节点的访问顺序而言的。
先序就是先第一个访问根节点
中序就是第二个访问根节点
后序就是最后一个访问根节点
但是不论怎么遍历,访问一棵树,总是从第一层的根节点开始的
前中后序也叫深度优先遍历
属于递归方式

4、层序遍历,也叫广度优先遍历
核心思路:一层带下一层


定义一个队列,每次出一个节点,就把该节点的左右孩子放进去,依次循环

typedef 重定义结构体只是多加了一个ytpedef,需要有完整的结构,要深入的理解typedef的意义

如果对一个代码不理解,或者对一个递归不理解
就画一个函数递归展开图

三、二叉树性质:


1、若规定根节点的层数为1,则一颗非空二叉树的i层上最多有2^(i-1)个节点
2、规定根节点的层数为1,则深度为h的二叉树的最大节点数是2^k -1
3、对任意一颗二叉树,如果度为0的叶节点数为n0,度为2的分支节点的个数为n2则有n0=n2+1;
4、规定根节点层数为1,具有n个节点的满二叉树的深度为h=log2N
5、一棵有N个节点的树有N-1条边
6、二叉树深度:k=log2(N+1)

满二叉树:每一层都是的节点都是满的

满二叉树节点数N:N = 2^k -1(N为节点数)
完全二叉树:前k-1都是满的,只有最后一层不满,但是最后一层的节点依然要求有顺序,即从左到右连续


四、相关计算


计算一颗完全二叉树的高度:
h=log2(N+1+x)//最后一层缺了X个节点
由于x不可能超过N,所以,可以近似认为,完全二叉树的高度就是log2(N+1)
而事实上,log2(N+1)和log2(N)没什么区别
所以当求一个完全二叉树的深度时,k=log2N,取低位整数

普通二叉树的增删查改没有实际意义。

五、搜索二叉树:任何一颗树的左子树都比它小,右子树都比它大


这样的结构非常适用于进行搜索:找一个数字,比节点大,只可能在它的右子树,比节点小,只可能在左子树,以此类推,画图,一目了然。
增加也是同样的道理。先加左边,再加右边
搜索查找一个数,最多查找数次为其高度,效率非常高

六、二叉树的存储结构

1、顺序存储,也就是使用数组来存储。但是一般用来存储完全二叉树,如果不是完全二叉树就会造成空间的浪费

2、链式结构
有两种:一种是二叉链,一种是三叉链
二叉链就是只有两个指针,一个指向左孩子,一个指向右孩子
三叉链就是有三个指针,多一个指针指向parent

七、二叉树基本操作


1、计算二叉树的节点数:
TrreeSize();
访问一个节点,不为空,++size
一种方式是:
定义一个全局变量,再对全局变量进行++zise(前置++相较于后置++略微高效)
但是如要调用第二次调用这个函数计算其他的二叉树,就会出现累加,在使用前都要对size置0,有问题。
同时,如果同时调用多个这个函数,size只有一个,有问题,这关系到多线程的问题
所以,使用传参的方式比较合适,即你计算你的,我计算我的,互不干涉,但是注意传参数要传地址,否则Asize不会改变
还有一种方式:
return root == NULL ? 0 : TreeeSize(root->left) + TreeSize(righe) + 1;
博客画图分析,画函数递归图,
这就是一个遍历思想的后序思想

2、求叶子叶节点的个数:
if(root ==Null) return 0;
if(root->left==NULL && root->right ==NLL) return 1;
return TreeSize(root->left) + TreeSize(root->right);

3、先序遍历

4、中序遍历

5、后续遍历

6、层序遍历

理解了写不出来就是没有理解,或者只是理解了一部分

7、非递归层序遍历

先序和中序可以还原一一颗树:先序可以确定根,中序可以确定左右子树
但是先序和后序不可以
后序加中序也是可以得到:后续最后一个可以确定根,中国可以确定左右子树

递归要注意传址,否则返回时就会销毁,每调用一个函数就是一个函数栈帧,这个函数里面的数据的改变不会影响上一层递归函数的值
即每一层都有一个i,下一层的++i不会影响上一层的i

八、源码(有需要的同学,可以直接拿去交作业了)

队列的层序遍历需要用到队列,所以我们需要单独创建一个队列相关的功能函数。C语言麻烦就麻烦在这里,造轮子比较麻烦。

头文件:

  1. #pragma once
  2. #include<stdio.h>
  3. #include<assert.h>
  4. #include<stdlib.h>
  5. typedef struct BinaryTreeNode* QDataType;
  6. // 链式结构:表示队列
  7. typedef struct QueueNode
  8. {
  9. struct QListNode* next;
  10. QDataType data;
  11. }QNode;
  12. // 队列的结构
  13. typedef struct Queue
  14. {
  15. QNode* front;//头
  16. QNode* rear;//尾
  17. int size;
  18. }Queue;
  19. // 初始化队列
  20. void QueueInit(Queue* q);
  21. // 队尾入队列
  22. void QueuePush(Queue* q, QDataType data);
  23. // 队头出队列
  24. void QueuePop(Queue* q);
  25. // 获取队列头部元素
  26. QDataType QueueFront(Queue* q);
  27. // 获取队列队尾元素
  28. QDataType QueueBack(Queue* q);
  29. // 获取队列中有效元素个数
  30. int QueueSize(Queue* q);
  31. // 检测队列是否为空,如果为空返回非零结果,如果非空返回0
  32. int QueueEmpty(Queue* q);
  33. // 销毁队列
  34. void QueueDestroy(Queue* q);
  1. #pragma once
  2. #include<stdio.h>
  3. #include<stdlib.h>
  4. #include<assert.h>
  5. #include<stdbool.h>
  6. typedef char BTDataType;
  7. typedef struct BinaryTreeNode
  8. {
  9. BTDataType data;
  10. struct BinaryTreeNode* left;
  11. struct BinaryTreeNode* right;
  12. }BTNode;
  13. // 通过前序遍历的数组"ABD##E#H##CF##G##"构建二叉树
  14. BTNode* BinaryTreeCreate(BTDataType* a,int* pi);
  15. // 二叉树销毁
  16. void BinaryTreeDestory(BTNode* root);
  17. // 二叉树节点个数
  18. int BinaryTreeSize(BTNode* root);
  19. // 二叉树叶子节点个数
  20. int BinaryTreeLeafSize(BTNode* root);
  21. // 二叉树第k层节点个数
  22. int BinaryTreeLevelKSize(BTNode* root, int k);
  23. // 二叉树查找值为x的节点
  24. BTNode* BinaryTreeFind(BTNode* root, BTDataType x);
  25. // 二叉树前序遍历
  26. void BinaryTreePrevOrder(BTNode* root);
  27. // 二叉树中序遍历
  28. void BinaryTreeInOrder(BTNode* root);
  29. // 二叉树后序遍历
  30. void BinaryTreePostOrder(BTNode* root);
  31. // 层序遍历
  32. //void BinaryTreeLevelOrder(BTNode* root);
  33. // 判断二叉树是否是完全二叉树
  34. bool BinaryTreeComplete(BTNode* root);

实现函数文件:

  1. #include"BinaryTree.h"
  2. #include"queue.h"
  3. // 通过前序遍历的数组"ABD##E#H##CF##G##"构建二叉树
  4. BTNode* BinaryTreeCreate(BTDataType* a, int* pi)
  5. {
  6. if (a[(*pi)] == '#')
  7. {
  8. (*pi)++;
  9. return NULL;
  10. }
  11. BTNode* root = (BTNode*)malloc(sizeof(BTNode));
  12. if (root == NULL)
  13. {
  14. perror("malloc fail \n");
  15. exit(-1);
  16. }
  17. root->data = a[(*pi)++];
  18. root->left = BinaryTreeCreate(a,pi);
  19. root->right = BinaryTreeCreate(a,pi);
  20. printf("%c ", root->data);
  21. return root;
  22. }
  23. // 二叉树前序遍历
  24. void BinaryTreePrevOrder(BTNode* root)
  25. {
  26. if (root == NULL) {
  27. printf("NULL ");
  28. return;
  29. }
  30. printf("%d ", root->data);
  31. BinaryTreePrevOrder(root->left);
  32. BinaryTreePrevOrder(root->right);
  33. }
  34. // 二叉树中序遍历
  35. void BinaryTreeInOrder(BTNode* root)
  36. {
  37. if (root == NULL) {
  38. printf("NULL ");
  39. return;
  40. }
  41. BinaryTreeInOrder(root->left);
  42. printf("%d ", root->data);
  43. BinaryTreeInOrder(root->right);
  44. }
  45. // 二叉树后序遍历
  46. void BinaryTreePostOrder(BTNode* root)
  47. {
  48. if (root == NULL) {
  49. printf("NULL ");
  50. return;
  51. }
  52. BinaryTreePostOrder(root->left);
  53. BinaryTreePostOrder(root->right);
  54. printf("%d ", root->data);
  55. }
  56. // 层序遍历
  57. void BinaryTreeLevelOrder(BTNode* root)
  58. {
  59. Queue q;
  60. QueueInit(&q);
  61. if (root )
  62. QueuePush(&q, root);
  63. //根、左子树、右子树
  64. //当前节点入队列,然后出队列,再把当前节点的左右子节点入队列
  65. int levelsize = 1;
  66. while (!QueueEmpty(&q))
  67. {
  68. while(levelsize--)
  69. {
  70. BTNode* front = QueueFront(&q);
  71. QueuePop(&q);
  72. printf("%d ", front->data);
  73. if (front->left)
  74. QueuePush(&q, front->left);
  75. if (front->right)
  76. QueuePush(&q, front->right);
  77. }
  78. printf("\n");
  79. levelsize = QueueSize(&q);
  80. }
  81. QueueDestroy(&q);
  82. }
  83. // 二叉树销毁
  84. void BinaryTreeDestory(BTNode* root)
  85. {
  86. if (root == NULL)
  87. return;
  88. BinaryTreeDestory(root->left);
  89. BinaryTreeDestory(root->right);
  90. free(root);
  91. root = NULL;
  92. printf("\n销毁成功 ");
  93. }
  94. // 二叉树节点个数
  95. int BinaryTreeSize(BTNode* root)
  96. {
  97. //分治:根、左子树、右子树
  98. //等于左子树的节点个数加上右子树的节点个数
  99. if (root == NULL)
  100. return 0;
  101. return BinaryTreeSize(root->left) + BinaryTreeSize(root->right) + 1;
  102. }
  103. // 二叉树叶子节点个数
  104. int BinaryTreeLeafSize(BTNode* root)
  105. {
  106. //左子树的叶子节点+右子树的叶子节点个数
  107. if (root == NULL)
  108. return 0;
  109. if (root->left == NULL && root->right == NULL)
  110. return 1;
  111. return BinaryTreeLeafSize(root->left) + BinaryTreeLeafSize(root->right);
  112. }
  113. // 二叉树第k层节点个数
  114. int BinaryTreeLevelKSize(BTNode* root, int k)
  115. {
  116. //分治
  117. //找K层的节点个数,等于K-1层
  118. //找第三层的节点个数,就等于找第二层的下一层节点个数
  119. //如果当前已经是第K层,那就直接返回1
  120. //对于每一个节点进行判断,如果这个节点是第k层,那么返回1,不用继续往下
  121. //如果这个节点不是k层,那么就继续往下,判断他的左子树和右子树
  122. assert(k);
  123. if (root == NULL)
  124. return 0;
  125. //如果是第K层
  126. if (k - 1 == 0)
  127. return 1;
  128. //不是第k层
  129. return BinaryTreeLevelKSize(root->left, k - 1) + BinaryTreeLevelKSize(root->right, k - 1);
  130. }
  131. // 二叉树查找值为x的节点
  132. BTNode* BinaryTreeFind(BTNode* root, BTDataType x)
  133. {
  134. //对每一个节点进行检查
  135. //当前节点不是,找左子树和右子树
  136. //找到了,返回当前节点的值
  137. if (root == NULL)
  138. return NULL;
  139. if (root->data == x)
  140. return root;
  141. BTNode* ret1 = BinaryTreeFind(root->left, x);
  142. if (ret1)
  143. return ret1;
  144. BTNode* ret2 = BinaryTreeFind(root->right, x);
  145. if (ret2)
  146. return ret2;
  147. return NULL;
  148. }
  149. // 判断二叉树是否是完全二叉树
  150. bool BinaryTreeComplete(BTNode* root)
  151. {
  152. //依旧是进入队列
  153. //如果出队列遇到空,如果此时队列为空,就是完全二叉树
  154. //如果队列不为空,就不是完全二叉树、
  155. Queue q;
  156. QueueInit(&q);
  157. if (root == NULL)
  158. return 1;
  159. QueuePush(&q,root);
  160. //根、左子树、右子树
  161. //在层序的基础上+
  162. while (!QueueEmpty(&q))
  163. {
  164. BTNode* front = QueueFront(&q);
  165. QueuePop(&q);
  166. if (front == NULL)
  167. break;
  168. QueuePush(&q, front->left);
  169. QueuePush(&q, front->right);
  170. }
  171. //前面遇到空,跳出,如果后面还有非空的数据,那就不是完全二叉树
  172. //仅仅判断非空不行,因为空的二叉树节点也push了,此时就不是空
  173. while (!QueueEmpty(&q))
  174. {
  175. BTNode* front = QueueFront(&q);
  176. QueuePop(&q);
  177. if (front)
  178. return false;
  179. }
  180. QueueDestroy(&q);
  181. return true;
  182. }
  1. #include"queue.h"
  2. #pragma once
  3. #include<stdio.h>
  4. #include<assert.h>
  5. #include<stdlib.h>
  6. // 初始化队列
  7. void QueueInit(Queue* q)
  8. {
  9. q->front = q->rear = NULL;
  10. q->size = 0;
  11. }
  12. // 队尾入队列
  13. void QueuePush(Queue* q, QDataType data)
  14. {
  15. if (q == NULL)
  16. return;
  17. if ( q->front == NULL)
  18. {
  19. QNode* temp = (QNode*)malloc(sizeof(QNode));
  20. if (temp == NULL)
  21. {
  22. perror("malloc fail!\n");
  23. exit(-1);
  24. }
  25. temp->data = data;
  26. temp->next = NULL;
  27. q->front = q->rear = temp;
  28. q->size++;
  29. }
  30. else
  31. {
  32. QNode* temp = (QNode*)malloc(sizeof(QNode));
  33. if (temp == NULL)
  34. {
  35. perror("malloc fail!\n");
  36. exit(-1);
  37. }
  38. temp->data = data;
  39. temp->next = NULL;
  40. q->rear->next = temp;
  41. q->rear = temp;
  42. q->size++;
  43. }
  44. }
  45. // 队头出队列
  46. void QueuePop(Queue* q)
  47. {
  48. assert(q);
  49. if (q->front == NULL)
  50. {
  51. return;
  52. }
  53. QNode* second = q->front->next;
  54. free(q->front);
  55. q->front = second;
  56. q->size--;
  57. }
  58. // 获取队列头部元素
  59. QDataType QueueFront(Queue* q)
  60. {
  61. assert(q);
  62. if(q->rear == NULL)
  63. {
  64. return;
  65. }
  66. return q->front->data;
  67. }
  68. // 获取队列队尾元素
  69. QDataType QueueBack(Queue* q)
  70. {
  71. assert(q);
  72. if (q->rear == NULL)
  73. {
  74. return;
  75. }
  76. return q->rear->data;
  77. }
  78. // 获取队列中有效元素个数
  79. int QueueSize(Queue* q)
  80. {
  81. assert(q);
  82. return q->size;
  83. }
  84. // 检测队列是否为空,如果为空返回非零结果,如果非空返回0
  85. int QueueEmpty(Queue* q)
  86. {
  87. assert(q);
  88. if (q->size == 0)
  89. {
  90. return 1;
  91. }
  92. else
  93. {
  94. return 0;
  95. }
  96. }
  97. // 销毁队列
  98. void QueueDestroy(Queue* q)
  99. {
  100. assert(q);
  101. if (q->rear == NULL)
  102. {
  103. return;
  104. }
  105. QNode* cur = q->front;
  106. while (q->size--)
  107. {
  108. QNode* next = cur->next;
  109. free(cur);
  110. cur = next;
  111. }
  112. }

测试文件:

  1. #include"BinaryTree.h"
  2. #include"queue.h"
  3. BTNode* BuyBinaryTreeNode(int date)
  4. {
  5. BTNode* newnode = (BTNode*)malloc(sizeof(BTNode));
  6. if (newnode == NULL)
  7. {
  8. perror("malloc fail");
  9. exit(-1);
  10. }
  11. newnode->data = date;
  12. newnode->left = NULL;
  13. newnode->right = NULL;
  14. return newnode;
  15. }
  16. int main()
  17. {
  18. BTNode* node1 = BuyBinaryTreeNode(1);
  19. BTNode* node2 = BuyBinaryTreeNode(2);
  20. BTNode* node3 = BuyBinaryTreeNode(3);
  21. BTNode* node4 = BuyBinaryTreeNode(4);
  22. BTNode* node5 = BuyBinaryTreeNode(5);
  23. BTNode* node6 = BuyBinaryTreeNode(6);
  24. BTNode* node7 = BuyBinaryTreeNode(7);
  25. BTNode* node8 = BuyBinaryTreeNode(8);
  26. node1->left = node2;
  27. node1->right = node3;
  28. node2->left = node4;
  29. node2->right = node5;
  30. node3->right = node6;
  31. node3->left = node7;
  32. printf("前序:");
  33. BinaryTreePrevOrder(node1);
  34. printf("\n");
  35. printf("中序:");
  36. BinaryTreeInOrder(node1);
  37. printf("\n");
  38. printf("后序:");
  39. BinaryTreePostOrder(node1);
  40. printf("\n");
  41. printf("层序:\n");
  42. BinaryTreeLevelOrder(node1);
  43. printf("\n");
  44. printf("叶子节点个数:%d\n", BinaryTreeLeafSize(node1));
  45. printf("第%d层节点个数:%d\n",2,BinaryTreeLevelKSize(node1, 2));
  46. //查找节点
  47. if (BinaryTreeFind(node1, 0))
  48. printf("存在:%d\n", BinaryTreeFind(node1, 0)->data);
  49. else
  50. printf("不存在该节点\n");
  51. //判断完全二叉树
  52. int ret = BinaryTreeComplete(node1);
  53. if(ret)
  54. printf("是完全二叉树\n");
  55. else
  56. printf("不是完全二叉树\n");
  57. BTDataType a[] = "ABD##E#H##CF##G##";
  58. int i = 0;
  59. BinaryTreeCreate(a,&i);
  60. BinaryTreeDestory(node1);
  61. return 0;
  62. }

运行截图:

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

闽ICP备14008679号