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

末蓝、 2022-02-25 15:48 342阅读 0赞

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

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

20130507202022904

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的时候,说明左右子树都已输出,所以该节点也出栈打印出来。

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

20130507203551093

具体的代码实现如下:

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

运行结果如下:

20130508220440152

发表评论

表情:
评论列表 (有 0 条评论,342人围观)

还没有评论,来说两句吧...

相关阅读