C语言实现二叉树的递归遍历与非递归遍历

 本文实现了对二叉树的递归遍历和非递归遍历,当然还包括了一些栈操作。

          二叉树的遍历本质上其实就是入栈出栈的问题,递归算法简单且容易理解,但是效率始终是个问题。非递归算法可以清楚的知道每步实现的细节,但是乍一看不想递归算法那么好理解,各有各的好处吧。接下来根据下图讲讲树的遍历。

《C语言实现二叉树的递归遍历与非递归遍历》

          1、先序遍历:先序遍历是先输出根节点,再输出左子树,最后输出右子树。上图的先序遍历结果就是:ABCDEF

          2、中序遍历:中序遍历是先输出左子树,再输出根节点,最后输出右子树。上图的中序遍历结果就是:CBDAEF

          3、后序遍历:后序遍历是先输出左子树,再输出右子树,最后输出根节点。上图的后序遍历结果就是:CDBFEA

         其中,后序遍历的非递归算法是最复杂的,我用了一个标识符isOut来表明是否需要弹出打印。因为只有当节点的左右子树都打印后该节点 才能弹出栈打印,所以标识isOut为1时打印,isOut初始值为0,这主要是为了处理非叶子节点。由后序遍历的原理决定,左右子树都被打印该节点才能打印,所以该节点肯定会被访问2次,第一次的时候不要打印,第二次打印完右子树的时候打印。叶子节点打印完后将isOut置为1。(纯粹是自己想的,应该还有逻辑更简单的算法)

         isOut处理具体如下:

  •  所有节点入栈的时候初始化为0;
  •  叶子节点打印输出后将isOut置为1;
  • 非叶子节点分两种情况。如果存在左子树,则输出左子树后将isOut置为1,此时指针已获得其右子树节点;如果不存在左子树,则将isOut置为1,此时指针已获得其右子树节点;在代码中分别体现在                                                                                                                                                                                                                                                     if ( (p->lchild) && (p->lchild->isOut == 1) )                                                                                                                                                                                                                            {//如果存在左子树,并且左子树已经遍历完,则说明该节点已经入栈,不用再次Push,直接走向右子树          
           p->isOut = 1;
           p = p->rchild;   
     }和                                                                                                                                                                                                                                                                                                 if (!StackEmpty(s))
      {
            GetTop(s,p);
            if ( p->lchild == NULL )
            p->isOut = 1; //右子树已输出,将父节点isOut置1
       };
  • 遇到isOut=1的时候,说明左右子树都已输出,所以该节点也出栈打印出来。

         以中序遍历为例,看看栈的内容是如何变化的:

         《C语言实现二叉树的递归遍历与非递归遍历》

          具体的代码实现如下:

      

[cpp] 
view plain
 copy

  1. #include<stdio.h>  
  2. #include<stdlib.h>  
  3.   
  4. #define STACKINITSIZE 100  
  5. #define STACKINCREASESIZE 20  
  6.   
  7. typedef char ElemType;  
  8. //树结构  
  9. typedef struct tree  
  10. {  
  11.     ElemType data;  
  12.     struct tree * lchild;  
  13.     struct tree * rchild;  
  14.     unsigned int isOut;   //专为后序遍历设置的,0为不需要被输出,1为需要被输出  
  15. }TreeNode,*Tree;  
  16.   
  17. //栈结构  
  18. typedef struct stack  
  19. {  
  20.     Tree * base;  
  21.     Tree * top;  
  22.     int stacksize;  
  23. }Sqstack;  
  24.   
  25.   
  26. /*****************栈的操作声明********************/  
  27.   
  28. //初始化栈  
  29. void InitStack( Sqstack &s );  
  30. //元素入栈  
  31. void Push( Sqstack &s, Tree e );  
  32. //获得栈顶元素  
  33. void GetTop( Sqstack s, Tree &e );  
  34. //弹出栈顶元素  
  35. void Pop( Sqstack &s, Tree &e );  
  36. //判断栈是否为空,为空返回1,否则返回0  
  37. int StackEmpty( Sqstack s );  
  38.   
  39. /*****************栈的操作声明********************/  
  40.   
  41.   
  42. /*****************树的操作声明********************/  
  43. //创建树,以先序序列建立树  
  44. void CreateTree(Tree &t);  
  45. //递归先序遍历  
  46. void PreOrder(Tree t);  
  47. //非递归先序遍历  
  48. void PreOrder1(Tree t);  
  49. //递归中序遍历  
  50. void InOrder(Tree t);  
  51. //非递归中序遍历  
  52. void InOrder1(Tree t);  
  53. //递归后序遍历  
  54. void PostOrder(Tree t);  
  55. //非递归后序遍历  
  56. void PostOrder1(Tree t);  
  57. /*****************树的操作声明********************/  
  58.   
  59. int main()  
  60. {  
  61.     Tree T;  
  62.     printf(“\n按先序序列输入结点序列,’#’代表空:”);  
  63.     CreateTree(T);  
  64.   
  65.     printf(“\n非递归先序遍历的结果:”);  
  66.     PreOrder1(T);  
  67.     printf(“\n递归先序遍历的结果:  “);  
  68.     PreOrder(T);  
  69.   
  70.     printf(“\n非递归中序遍历的结果:”);  
  71.     InOrder1(T);  
  72.     printf(“\n递归中序遍历的结果:  “);  
  73.     InOrder(T);  
  74.   
  75.     printf(“\n非递归后序遍历的结果:”);  
  76.     PostOrder1(T);  
  77.     printf(“\n递归后序遍历的结果:  “);  
  78.     PostOrder(T);  
  79.     printf(“\n”);  
  80.   
  81. }  
  82.   
  83.   
  84. /*****************栈的操作定义********************/  
  85.   
  86. //初始化栈  
  87. void InitStack( Sqstack &s )  
  88. {  
  89.     s.base = (Tree *)malloc(STACKINITSIZE*sizeof(Tree));  
  90.     if ( !s.base )  
  91.     {  
  92.     printf(“InitStack内存分配出错\n”);  
  93.     }      
  94.     s.top = s.base;  
  95.     s.stacksize = STACKINITSIZE;  
  96. }  
  97.   
  98. //元素入栈  
  99. void Push( Sqstack &s, Tree e )  
  100. {  
  101.     if ( s.top – s.base >= s.stacksize )  
  102.     {  
  103.     s.base = (Tree *)realloc(s.base,(s.stacksize+STACKINCREASESIZE)*sizeof(Tree));  
  104.     if ( !s.base )  
  105.         {  
  106.         printf(“Push内存分配出错\n”);  
  107.         return ;  
  108.         }  
  109.       
  110.      s.top = s.base + s.stacksize;  
  111.     s.stacksize += STACKINCREASESIZE;  
  112.     }  
  113.     e->isOut = 0;  
  114.     *s.top++ = e;  
  115. }  
  116.   
  117. //获得栈顶元素  
  118. void GetTop( Sqstack s, Tree &e )  
  119. {  
  120.     e = *(s.top – 1);  
  121. }  
  122.   
  123. //弹出栈顶元素  
  124. void Pop( Sqstack &s, Tree &e )  
  125. {  
  126.     if ( s.top == s.base )  
  127.     {  
  128.     printf(“栈为空\n”);  
  129.     return ;  
  130.     }  
  131.     e = *(–s.top);  
  132. }  
  133.   
  134. //判断栈是否为空,为空返回1,否则返回0  
  135. int StackEmpty( Sqstack s )  
  136. {  
  137.     if ( s.top == s.base )  
  138.     return 1;  
  139.     return 0;  
  140. }  
  141.   
  142. /*****************栈的操作定义********************/  
  143.   
  144.   
  145.   
  146. /*****************树的操作定义********************/  
  147. //创建树,以先序序列建立树  
  148. void CreateTree(Tree &t)  
  149. {  
  150.     char ch;  
  151.     scanf(“%c”,&ch);  
  152.     if ( ch == ‘#’ )  
  153.     t = NULL;  
  154.     else  
  155.     {  
  156.     t = (Tree)malloc(sizeof(TreeNode));  
  157.         if ( !t )  
  158.     {  
  159.         printf(“分配内存出错!”);  
  160.         return ;  
  161.     }  
  162.     t->data = ch;  
  163.     CreateTree(t->lchild);  
  164.     CreateTree(t->rchild);  
  165.     }  
  166. }  
  167.   
  168.   
  169. //递归先序遍历  
  170. void PreOrder(Tree t)  
  171. {  
  172.     if ( t )  
  173.     {  
  174.     printf(“%c”,t->data);  
  175.     PreOrder(t->lchild);  
  176.     PreOrder(t->rchild);  
  177.     }  
  178. }  
  179.   
  180. //非递归先序遍历  
  181. void PreOrder1(Tree t)  
  182. {  
  183.     Tree p = t;  
  184.     Sqstack s;  
  185.     InitStack(s);  
  186.   
  187.     while ( p || !StackEmpty(s) )  
  188.     {  
  189.     if ( p )  
  190.     {  
  191.         printf(“%c”,p->data);  
  192.         Push(s,p);  
  193.         p = p->lchild;      
  194.     }  
  195.     else  
  196.     {  
  197.         Pop(s,p);   
  198.         p = p->rchild;  
  199.     }  
  200.     }  
  201.   
  202. }  
  203.   
  204.   
  205. //递归中序遍历  
  206. void InOrder(Tree t)  
  207. {  
  208.     if ( t )  
  209.     {  
  210.     InOrder(t->lchild);  
  211.     printf(“%c”,t->data);  
  212.     InOrder(t->rchild);  
  213.     }  
  214. }  
  215.   
  216.   
  217. //非递归中序遍历  
  218. void InOrder1(Tree t)  
  219. {  
  220.     Tree p = t;  
  221.     Sqstack s;  
  222.     InitStack(s);    
  223.       
  224.     while ( p || !StackEmpty(s) )  
  225.     {  
  226.     if ( p )  
  227.         {  
  228.         Push(s,p);  
  229.         p = p->lchild;  
  230.     }      
  231.     else  
  232.     {  
  233.         Pop(s,p);  
  234.         printf(“%c”,p->data);  
  235.         p = p->rchild;  
  236.     }   
  237.     }  
  238. }  
  239.   
  240. //递归后序遍历  
  241. void PostOrder(Tree t)  
  242. {  
  243.     if ( t )  
  244.     {  
  245.     PostOrder(t->lchild);  
  246.      PostOrder(t->rchild);  
  247.     printf(“%c”,t->data);  
  248.     }  
  249. }  
  250.   
  251.   
  252. //非递归后序遍历  
  253. void PostOrder1(Tree t)  
  254. {  
  255.     t->isOut = 0;  
  256.     Tree p = t;  
  257.     Sqstack s;  
  258.     InitStack(s);   
  259.   
  260.     while ( p || !StackEmpty(s) )  
  261.     {  
  262.     if ( p )  
  263.         {  
  264.         if ( p->isOut )  
  265.             {//左右子树都已输出,则该节点也输出          
  266.             Pop(s,p);  
  267.             printf(“%c”,p->data);  
  268.         if (!StackEmpty(s))  
  269.                 GetTop(s,p); //得到弹出节点元素的父节点  
  270.             else  
  271.             p = NULL;  
  272.         }  
  273.         else  
  274.         {      
  275.             if ( (p->lchild) && (p->lchild->isOut == 1) )  
  276.             {//如果存在左子树,并且左子树已经遍历完,则说明该节点已经入栈,不用再次Push,直接走向右子树            
  277.             p->isOut = 1;  
  278.             p = p->rchild;     
  279.               }  
  280.             else  
  281.             {  
  282.             Push(s,p);  
  283.             p = p->lchild;  
  284.             }            
  285.         }  
  286.         }  
  287.         else  
  288.         {  
  289.         if (!StackEmpty(s))  
  290.             GetTop(s,p);   
  291.         else  
  292.         p = NULL;  
  293.   
  294.         if ( p->rchild )  
  295.         {  
  296.             p = p->rchild;  
  297.         }  
  298.         else  
  299.         {  
  300.            Pop(s,p);  
  301.             printf(“%c”,p->data);  
  302.             p->isOut = 1;  
  303.             if (!StackEmpty(s))  
  304.             {  
  305.             GetTop(s,p);  
  306.             if ( p->lchild == NULL )  
  307.                 p->isOut = 1; //右子树已输出,将父节点isOut置1  
  308.         }  
  309.             else  
  310.             p = NULL;  
  311.         }  
  312.           
  313.         }  
  314.     }  
  315.       
  316. }  

         运行结果如下:

《C语言实现二叉树的递归遍历与非递归遍历》






    原文作者:B树
    原文地址: https://blog.csdn.net/B_boyi/article/details/51404599
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞