当前位置:   article > 正文

二叉树的遍历及深度的计算_二叉树深度计算

二叉树深度计算

目录

前言

一、后续遍历的简单版本

二、层序遍历

三、计算树的深度

1.什么是树的深度

2.递归

3.非递归

总结




前言

我们上一篇已经介绍了大量的二叉树知识,这下我们介绍一下其他的知识,接下来我们会了解到二叉树非递归后序遍历的简单版本,还有层序遍历,以及后面的二叉树的深度查找递归,非递归两种版本。这些都会帮助我们在以后的问题中解决很多的事的,所以接下来就要慢慢思考,它里面把好的整体思想。


一、后续遍历的简单版本

之前我们已经了解到后序遍历的两种算法,但是他的非递归算法的难度还是比较大的,所以接下来了解一个easy版本,我们首先按照 跟->右->左 先把所有的节点数据存储到数组里面,然后把我们的数组里面的数组反转一下输出,这样就达到了我们的目的。在这里我们需要用到数组存储,其他的很多思想都是比较容易理解的。

  1. void Nrelasteasy(Tree* tree)//非递归 后序遍历(easy)
  2. {
  3. //定义栈,为了操作树的存储
  4. Tree* stack[MAX],*node;
  5. //定义数组,方便后续进行的输出
  6. int array[MAX]={0};
  7. //初始化对列,count用来记录一共有多少个数
  8. int top=0,count=0,i;
  9. //判断仅从遍历的树是否为空,如果为空的话,就返回
  10. if(tree==NULL){
  11. printf("该树为空\n");
  12. return;
  13. }else{
  14. //为了方便,在栈的第一位不存数据
  15. top++;
  16. //将树的根节点赋值给栈的第一位
  17. stack[top]=tree;
  18. //如果栈为为空,我们就退出整个循环
  19. while(top>0){
  20. //取出栈顶元素
  21. node=stack[top];
  22. //对栈的指针进行操作
  23. top--;
  24. //将该节点的数据存到数组里面,同时我们要把数组的索引进行操作
  25. array[count++]=node->data;
  26. //因为栈是先进后出的,所以我们先把该节点的左孩子入栈,再把该节点的右孩子入栈
  27. if(node->l_child!=NULL){
  28. top++;
  29. stack[top]=node->l_child;
  30. }
  31. if(node->r_child!=NULL){
  32. top++;
  33. stack[top]=node->r_child;
  34. }
  35. }
  36. //最后就是要反着把所有的数组数据输出
  37. for(i=count-1;i>=0;i--){
  38. printf("%d\n",array[i]);
  39. }
  40. }
  41. }

二、层序遍历

二叉树的层序遍历是什么呢,他就是按照一层层的顺序把我们的所有节点都进行输出,不如上图中的结构,我们输出的顺序就是3—2,8—2,3,6—5—4,这样的顺序把说有的节点输出,按照这个顺序,我们很容易的想到队列这种数据结构,因为它可以把每层的数据依次存到队尾,然后利用队头输出之前存的节点,这样下来,就达到了我们的目的。

代码如下(示例):

  1. void levertraversal(Tree* tree)
  2. {
  3. //如果数组为空,那么我们就返回
  4. if(tree==NULL){
  5. printf("该树为空\n");
  6. return;
  7. }
  8. //定义队列,为了存储树的数据
  9. Tree *queue[MAX],*node;
  10. //初始化队列的指针
  11. int front=0,rear=0;
  12. //将树的根节点赋值给队列,同时将尾指针向后移动
  13. queue[rear++]=tree;
  14. //如果队列为空,我们就退出整个循环
  15. while(front!=rear){
  16. //取出队头元素
  17. node=queue[front++];
  18. //输出该节点的数据
  19. printf("%d\n",node->data);
  20. //如果该节点的左右有孩子,就将孩子入队,先将左孩子入队,再将右孩子入队,因为队列是先进先出的数据结构
  21. if(node->l_child != NULL){
  22. queue[rear++] = node->l_child;
  23. }
  24. if(node->r_child != NULL){
  25. queue[rear++] = node->r_child;
  26. }
  27. }
  28. }

三、计算树的深度

1.什么是树的深度

二叉树的根结点所在的层数为1,根结点的孩子结点所在的层数为2,以此下去。深度是指所有结点中最深的结点。 

从图中我们就很容易的看到该二叉树的深度为3,这个看起来很容易,但是算法中还是有一定的困难的,因为,我们不知道它里面哪一个是最深的,所以要遍历所有的节点,看到它的最深的,它里面含有很多的问题,所以,我们就要了解到具体的算法问题。接下里,就了解一下递归和非递归的算法。

2.递归

利用递归的算法,就是我们把树按照一遍遍历完,然后比较同一层中他们左右孩子的子孙深度多,然后返回深度多的,我们在返回的时候要进行加一操作,这样就表示该层是存在数据的,如果没有存在数据的话,那么返回的就是0.

代码如下(示例):

  1. int treedepth(Tree *tree)//树的深度
  2. {
  3. //定义两个数据,用来记录树的深度
  4. int depth1 = 0,depth2 = 0;
  5. //如果树为空,那么我们返回的数值就为0
  6. if(tree == NULL){
  7. return 0;
  8. }else{
  9. //对左右孩子进行继续的遍历
  10. depth1=treedepth(tree->l_child);
  11. depth2=treedepth(tree->r_child);
  12. //判断出左右孩子那个最大,然后要反回他的深度+1,如果没有的话会返回0,他的深度也不会增加
  13. return depth1>depth2?depth1+1:depth2+1;
  14. }
  15. }

3.非递归

在非递归中我们要引用队列进行操作,队列的操作会让我们在很多问题上比较方便,在这里需要注意的是,我们要用到一个数一直指向最后的索引,为什么要这样呢,因为这个索引是为了让我们只得知道他的这一层是否遍历过去,如果遍历过去,那么我们的头指针就会遇到这个索引,这个时候我们的层数就是达到了所有。也记录完了所有的深度。接着就是要让该索引指向新的尾指针。

如果大家不理解这个过程,可以画图尝试。

  1. int Nretreedepth(Tree *tree)//非递归 树的深度
  2. {
  3. //定义队列
  4. Tree *queue[MAX],*node;
  5. //为了方便操作,用node来操作
  6. node=tree;
  7. //初始化队列,depth用来记录他的深度,我们用level指向队列的队尾元素
  8. int front = 0,rear = 0,depth = 0,level;
  9. //如果树不为空,我们就把树的根节点赋值给队列的尾
  10. if(tree != NULL){
  11. queue[++rear] = node;
  12. }
  13. //让level指向队尾指针
  14. level=rear;
  15. //如果队为空的话,我们就退出整个循环
  16. while(front<rear){
  17. //取出队头元素,同时将队头指针向后移动
  18. node=queue[++front];
  19. //将该节点的左右孩子先入队,因为队列是先进先出,所以先将左孩子入队,再将右孩子入队
  20. if(node->l_child != NULL){
  21. queue[++rear] = node->l_child;
  22. }
  23. if(node->r_child != NULL){
  24. queue[++rear] = node->r_child;
  25. }
  26. //如果头指针等于之前标记的尾节点,那么说明这个层已经全部被遍历,所以这个深度需要加加
  27. if(front == level){
  28. depth++;
  29. //接着需要把这个让level指向当前的尾节点,方便后面的查找
  30. level = rear;
  31. }
  32. }
  33. //返回这个深度
  34. return depth;
  35. }

总结

今天看到的这些是比较重要的几个树的操作,里面有很多的细节,数组索引的移动方式,队列,栈的数据结构,都是我们需要注意的地方,这让我们有很大的帮助,通过这些,在尽头的有些问题上可以很好地解决。

接下来就是附上今天的所有代码。

  1. #include<stdio.h>
  2. #include<stdlib.h>
  3. #define MAX 10
  4. typedef struct node{
  5. int data;
  6. struct node *l_child;
  7. struct node *r_child;
  8. }Tree;
  9. Tree *Create_Tree();//创建二叉树
  10. void Nrelasteasy(Tree* tree);//非递归 后序遍历(easy)
  11. void levertraversal(Tree* tree);//层次遍历
  12. int treedepth(Tree *tree);//树的深度
  13. int Nretreedepth(Tree *tree);//非递归 树的深度
  14. int main()
  15. {
  16. Tree*tree;
  17. tree=Create_Tree();
  18. printf("非递归后序遍历,简单版本\n");
  19. Nrelasteasy(tree);
  20. printf("层次遍历\n");
  21. levertraversal(tree);
  22. printf("递归计算树的深度%d\n",treedepth(tree));
  23. printf("非递归计算树的深度%d\n",Nretreedepth(tree));
  24. }
  25. Tree *Create_Tree()
  26. {
  27. //先申明一个空指针
  28. Tree *root=NULL;
  29. int data;
  30. scanf("%d", &data);//通过输入的ch是否为特殊符号来判断该节点是否有孩子节点
  31. //这里可以用任何的特殊字符,根据自己来定义
  32. if(data == -1){ //不存在孩子节点
  33. root=NULL;
  34. }else{
  35. //当这个数据需要存储的时候要,我们就需要离开开辟空间
  36. root = (Tree *)malloc(sizeof(Tree));
  37. //如果开辟失败,就返回
  38. if(NULL == root){
  39. printf("创建失败\n");
  40. return NULL;
  41. }
  42. //将数据存储到根节点里面
  43. root->data = data;
  44. root->l_child = Create_Tree();//存在左孩子节点,递归调用本函数,使得左孩子节点先被赋值
  45. root->r_child = Create_Tree();//存在右孩子节点,递归调用本函数,使得右孩子节点后被赋值
  46. }
  47. //最后将该节点返回
  48. return root;
  49. }
  50. void Nrelasteasy(Tree* tree)//非递归 后序遍历(easy)
  51. {
  52. //定义栈,为了操作树的存储
  53. Tree* stack[MAX],*node;
  54. //定义数组,方便后续进行的输出
  55. int array[MAX]={0};
  56. //初始化对列,count用来记录一共有多少个数
  57. int top=0,count=0,i;
  58. //判断仅从遍历的树是否为空,如果为空的话,就返回
  59. if(tree==NULL){
  60. printf("该树为空\n");
  61. return;
  62. }else{
  63. //为了方便,在栈的第一位不存数据
  64. top++;
  65. //将树的根节点赋值给栈的第一位
  66. stack[top]=tree;
  67. //如果栈为为空,我们就退出整个循环
  68. while(top>0){
  69. //取出栈顶元素
  70. node=stack[top];
  71. //对栈的指针进行操作
  72. top--;
  73. //将该节点的数据存到数组里面,同时我们要把数组的索引进行操作
  74. array[count++]=node->data;
  75. //因为栈是先进后出的,所以我们先把该节点的左孩子入栈,再把该节点的右孩子入栈
  76. if(node->l_child!=NULL){
  77. top++;
  78. stack[top]=node->l_child;
  79. }
  80. if(node->r_child!=NULL){
  81. top++;
  82. stack[top]=node->r_child;
  83. }
  84. }
  85. //最后就是要反着把所有的数组数据输出
  86. for(i=count-1;i>=0;i--){
  87. printf("%d\n",array[i]);
  88. }
  89. }
  90. }
  91. void levertraversal(Tree* tree)
  92. {
  93. //如果数组为空,那么我们就返回
  94. if(tree==NULL){
  95. printf("该树为空\n");
  96. return;
  97. }
  98. //定义队列,为了存储树的数据
  99. Tree *queue[MAX],*node;
  100. //初始化队列的指针
  101. int front=0,rear=0;
  102. //将树的根节点赋值给队列,同时将尾指针向后移动
  103. queue[rear++]=tree;
  104. //如果队列为空,我们就退出整个循环
  105. while(front!=rear){
  106. //取出队头元素
  107. node=queue[front++];
  108. //输出该节点的数据
  109. printf("%d\n",node->data);
  110. //如果该节点的左右有孩子,就将孩子入队,先将左孩子入队,再将右孩子入队,因为队列是先进先出的数据结构
  111. if(node->l_child != NULL){
  112. queue[rear++] = node->l_child;
  113. }
  114. if(node->r_child != NULL){
  115. queue[rear++] = node->r_child;
  116. }
  117. }
  118. }
  119. int treedepth(Tree *tree)//树的深度
  120. {
  121. //定义两个数据,用来记录树的深度
  122. int depth1 = 0,depth2 = 0;
  123. //如果树为空,那么我们返回的数值就为0
  124. if(tree == NULL){
  125. return 0;
  126. }else{
  127. //对左右孩子进行继续的遍历
  128. depth1=treedepth(tree->l_child);
  129. depth2=treedepth(tree->r_child);
  130. //判断出左右孩子那个最大,然后要反回他的深度+1,如果没有的话会返回0,他的深度也不会增加
  131. return depth1>depth2?depth1+1:depth2+1;
  132. }
  133. }
  134. int Nretreedepth(Tree *tree)//非递归 树的深度
  135. {
  136. //定义队列
  137. Tree *queue[MAX],*node;
  138. //为了方便操作,用node来操作
  139. node=tree;
  140. //初始化队列,depth用来记录他的深度,我们用level指向队列的队尾元素
  141. int front = 0,rear = 0,depth = 0,level;
  142. //如果树不为空,我们就把树的根节点赋值给队列的尾
  143. if(tree != NULL){
  144. queue[++rear] = node;
  145. }
  146. //让level指向队尾指针
  147. level=rear;
  148. //如果队为空的话,我们就退出整个循环
  149. while(front<rear){
  150. //取出队头元素,同时将队头指针向后移动
  151. node=queue[++front];
  152. //将该节点的左右孩子先入队,因为队列是先进先出,所以先将左孩子入队,再将右孩子入队
  153. if(node->l_child != NULL){
  154. queue[++rear] = node->l_child;
  155. }
  156. if(node->r_child != NULL){
  157. queue[++rear] = node->r_child;
  158. }
  159. //如果头指针等于之前标记的尾节点,那么说明这个层已经全部被遍历,所以这个深度需要加加
  160. if(front == level){
  161. depth++;
  162. //接着需要把这个让level指向当前的尾节点,方便后面的查找
  163. level = rear;
  164. }
  165. }
  166. //返回这个深度
  167. return depth;
  168. }
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/菜鸟追梦旅行/article/detail/566957
推荐阅读
相关标签
  

闽ICP备14008679号