转 算法之二叉树各种遍历

原文:http://blog.csdn.net/sjf0115/article/details/8645991

树形结构是一类重要的非线性数据结构,其中以树和二叉树最为常用。

二叉树是每个结点最多有两个子树的有序树。通常子树的根被称作“左子树”(left subtree)和“右子树”(right subtree)。二叉树常被用作二叉查找树和二叉堆或是二叉排序树。二叉树的每个结点至多只有二棵子树(不存在度大于2的结点),二叉树的子树有左右之分,次序不能颠倒。二叉树的第i层至多有2的 i -1次方个结点;深度为k的二叉树至多有2^(k) -1个结点;对任何一棵二叉树T,如果其终端结点数(即叶子结点数)为n0,度为2的结点数为n2,则n0 = n2 + 1。

二叉树的链式存储结构是一类重要的数据结构,其形式定义如下:

[cpp] view plaincopy

  1. //二叉树结点
  2. typedef struct BiTNode{
  3. //数据
  4. char data;
  5. //左右孩子指针
  6. struct BiTNode *lchild,*rchild;
  7. }BiTNode,*BiTree;

二叉树的创建:

通过读入一个字符串,建立二叉树的算法如下:

[cpp] view plaincopy

  1. //按先序序列创建二叉树
  2. int CreateBiTree(BiTree &T){
  3. char data;
  4. //按先序次序输入二叉树中结点的值(一个字符),‘#’表示空树
  5. scanf("%c",&data);
  6. if(data == ‘#‘){
  7. T = NULL;
  8. }
  9. else{
  10. T = (BiTree)malloc(sizeof(BiTNode));
  11. //生成根结点
  12. T->data = data;
  13. //构造左子树
  14. CreateBiTree(T->lchild);
  15. //构造右子树
  16. CreateBiTree(T->rchild);
  17. }
  18. return 0;
  19. }

二叉树的遍历:

遍历是对树的一种最基本的运算,所谓遍历二叉树,就是按一定的规则和顺序走遍二叉树的所有结点,使每一个结点都被访问一次,而且只被访问一次。由于二叉树是非线性结构,因此,树的遍历实质上是将二叉树的各个结点转换成为一个线性序列来表示。

递归算法:

[cpp] view plaincopy

  1. //输出
  2. void Visit(BiTree T){
  3. if(T->data != ‘#‘){
  4. printf("%c ",T->data);
  5. }
  6. }
  7. //先序遍历
  8. void PreOrder(BiTree T){
  9. if(T != NULL){
  10. //访问根节点
  11. Visit(T);
  12. //访问左子结点
  13. PreOrder(T->lchild);
  14. //访问右子结点
  15. PreOrder(T->rchild);
  16. }
  17. }
  18. //中序遍历
  19. void InOrder(BiTree T){
  20. if(T != NULL){
  21. //访问左子结点
  22. InOrder(T->lchild);
  23. //访问根节点
  24. Visit(T);
  25. //访问右子结点
  26. InOrder(T->rchild);
  27. }
  28. }
  29. //后序遍历
  30. void PostOrder(BiTree T){
  31. if(T != NULL){
  32. //访问左子结点
  33. PostOrder(T->lchild);
  34. //访问右子结点
  35. PostOrder(T->rchild);
  36. //访问根节点
  37. Visit(T);
  38. }
  39. }

非递归算法:

<1>先序遍历:

【思路】:访问T->data后,将T入栈,遍历左子树;遍历完左子树返回时,栈顶元素应为T,出栈,再先序遍历T的右子树。

[cpp] view plaincopy

  1. /* 先序遍历(非递归)
  2. 思路:访问T->data后,将T入栈,遍历左子树;遍历完左子树返回时,栈顶元素应为T,出栈,再先序遍历T的右子树。
  3. */
  4. void PreOrder2(BiTree T){
  5. stack<BiTree> stack;
  6. //p是遍历指针
  7. BiTree p = T;
  8. //栈不空或者p不空时循环
  9. while(p || !stack.empty()){
  10. if(p != NULL){
  11. //存入栈中
  12. stack.push(p);
  13. //访问根节点
  14. printf("%c ",p->data);
  15. //遍历左子树
  16. p = p->lchild;
  17. }
  18. else{
  19. //退栈
  20. p = stack.top();
  21. stack.pop();
  22. //访问右子树
  23. p = p->rchild;
  24. }
  25. }//while
  26. }

<2>中序遍历

【思路】:T是要遍历树的根指针,中序遍历要求在遍历完左子树后,访问根,再遍历右子树。          先将T入栈,遍历左子树;遍历完左子树返回时,栈顶元素应为T,出栈,访问T->data,再中序遍历T的右子树。

[cpp] view plaincopy

  1. void InOrder2(BiTree T){
  2. stack<BiTree> stack;
  3. //p是遍历指针
  4. BiTree p = T;
  5. //栈不空或者p不空时循环
  6. while(p || !stack.empty()){
  7. if(p != NULL){
  8. //存入栈中
  9. stack.push(p);
  10. //遍历左子树
  11. p = p->lchild;
  12. }
  13. else{
  14. //退栈,访问根节点
  15. p = stack.top();
  16. printf("%c ",p->data);
  17. stack.pop();
  18. //访问右子树
  19. p = p->rchild;
  20. }
  21. }//while
  22. }

<3>后序遍历

【思路】:T是要遍历树的根指针,后序遍历要求在遍历完左右子树后,再访问根。需要判断根结点的左右子树是否均遍历过。

[cpp] view plaincopy

  1. //后序遍历(非递归)
  2. typedef struct BiTNodePost{
  3. BiTree biTree;
  4. char tag;
  5. }BiTNodePost,*BiTreePost;
  6. void PostOrder2(BiTree T){
  7. stack<BiTreePost> stack;
  8. //p是遍历指针
  9. BiTree p = T;
  10. BiTreePost BT;
  11. //栈不空或者p不空时循环
  12. while(p != NULL || !stack.empty()){
  13. //遍历左子树
  14. while(p != NULL){
  15. BT = (BiTreePost)malloc(sizeof(BiTNodePost));
  16. BT->biTree = p;
  17. //访问过左子树
  18. BT->tag = ‘L‘;
  19. stack.push(BT);
  20. p = p->lchild;
  21. }
  22. //左右子树访问完毕访问根节点
  23. while(!stack.empty() && (stack.top())->tag == ‘R‘){
  24. BT = stack.top();
  25. //退栈
  26. stack.pop();
  27. BT->biTree;
  28. printf("%c ",BT->biTree->data);
  29. }
  30. //遍历右子树
  31. if(!stack.empty()){
  32. BT = stack.top();
  33. //访问过右子树
  34. BT->tag = ‘R‘;
  35. p = BT->biTree;
  36. p = p->rchild;
  37. }
  38. }//while
  39. }

<4>层次遍历

【思路】:按从顶向下,从左至右的顺序来逐层访问每个节点,层次遍历的过程中需要用队列。

[cpp] view plaincopy

  1. //层次遍历
  2. void LevelOrder(BiTree T){
  3. BiTree p = T;
  4. //队列
  5. queue<BiTree> queue;
  6. //根节点入队
  7. queue.push(p);
  8. //队列不空循环
  9. while(!queue.empty()){
  10. //对头元素出队
  11. p = queue.front();
  12. //访问p指向的结点
  13. printf("%c ",p->data);
  14. //退出队列
  15. queue.pop();
  16. //左子树不空,将左子树入队
  17. if(p->lchild != NULL){
  18. queue.push(p->lchild);
  19. }
  20. //右子树不空,将右子树入队
  21. if(p->rchild != NULL){
  22. queue.push(p->rchild);
  23. }
  24. }
  25. }

测试用例:

输入:

ABC##DE#G##F###

输出:

代码:

[cpp] view plaincopy

  1. #include<iostream>
  2. #include<stack>
  3. #include<queue>
  4. using namespace std;
  5. //二叉树结点
  6. typedef struct BiTNode{
  7. //数据
  8. char data;
  9. //左右孩子指针
  10. struct BiTNode *lchild,*rchild;
  11. }BiTNode,*BiTree;
  12. //按先序序列创建二叉树
  13. int CreateBiTree(BiTree &T){
  14. char data;
  15. //按先序次序输入二叉树中结点的值(一个字符),‘#’表示空树
  16. scanf("%c",&data);
  17. if(data == ‘#‘){
  18. T = NULL;
  19. }
  20. else{
  21. T = (BiTree)malloc(sizeof(BiTNode));
  22. //生成根结点
  23. T->data = data;
  24. //构造左子树
  25. CreateBiTree(T->lchild);
  26. //构造右子树
  27. CreateBiTree(T->rchild);
  28. }
  29. return 0;
  30. }
  31. //输出
  32. void Visit(BiTree T){
  33. if(T->data != ‘#‘){
  34. printf("%c ",T->data);
  35. }
  36. }
  37. //先序遍历
  38. void PreOrder(BiTree T){
  39. if(T != NULL){
  40. //访问根节点
  41. Visit(T);
  42. //访问左子结点
  43. PreOrder(T->lchild);
  44. //访问右子结点
  45. PreOrder(T->rchild);
  46. }
  47. }
  48. //中序遍历
  49. void InOrder(BiTree T){
  50. if(T != NULL){
  51. //访问左子结点
  52. InOrder(T->lchild);
  53. //访问根节点
  54. Visit(T);
  55. //访问右子结点
  56. InOrder(T->rchild);
  57. }
  58. }
  59. //后序遍历
  60. void PostOrder(BiTree T){
  61. if(T != NULL){
  62. //访问左子结点
  63. PostOrder(T->lchild);
  64. //访问右子结点
  65. PostOrder(T->rchild);
  66. //访问根节点
  67. Visit(T);
  68. }
  69. }
  70. /* 先序遍历(非递归)
  71. 思路:访问T->data后,将T入栈,遍历左子树;遍历完左子树返回时,栈顶元素应为T,出栈,再先序遍历T的右子树。
  72. */
  73. void PreOrder2(BiTree T){
  74. stack<BiTree> stack;
  75. //p是遍历指针
  76. BiTree p = T;
  77. //栈不空或者p不空时循环
  78. while(p || !stack.empty()){
  79. if(p != NULL){
  80. //存入栈中
  81. stack.push(p);
  82. //访问根节点
  83. printf("%c ",p->data);
  84. //遍历左子树
  85. p = p->lchild;
  86. }
  87. else{
  88. //退栈
  89. p = stack.top();
  90. stack.pop();
  91. //访问右子树
  92. p = p->rchild;
  93. }
  94. }//while
  95. }
  96. /* 中序遍历(非递归)
  97. 思路:T是要遍历树的根指针,中序遍历要求在遍历完左子树后,访问根,再遍历右子树。
  98. 先将T入栈,遍历左子树;遍历完左子树返回时,栈顶元素应为T,出栈,访问T->data,再中序遍历T的右子树。
  99. */
  100. void InOrder2(BiTree T){
  101. stack<BiTree> stack;
  102. //p是遍历指针
  103. BiTree p = T;
  104. //栈不空或者p不空时循环
  105. while(p || !stack.empty()){
  106. if(p != NULL){
  107. //存入栈中
  108. stack.push(p);
  109. //遍历左子树
  110. p = p->lchild;
  111. }
  112. else{
  113. //退栈,访问根节点
  114. p = stack.top();
  115. printf("%c ",p->data);
  116. stack.pop();
  117. //访问右子树
  118. p = p->rchild;
  119. }
  120. }//while
  121. }
  122. //后序遍历(非递归)
  123. typedef struct BiTNodePost{
  124. BiTree biTree;
  125. char tag;
  126. }BiTNodePost,*BiTreePost;
  127. void PostOrder2(BiTree T){
  128. stack<BiTreePost> stack;
  129. //p是遍历指针
  130. BiTree p = T;
  131. BiTreePost BT;
  132. //栈不空或者p不空时循环
  133. while(p != NULL || !stack.empty()){
  134. //遍历左子树
  135. while(p != NULL){
  136. BT = (BiTreePost)malloc(sizeof(BiTNodePost));
  137. BT->biTree = p;
  138. //访问过左子树
  139. BT->tag = ‘L‘;
  140. stack.push(BT);
  141. p = p->lchild;
  142. }
  143. //左右子树访问完毕访问根节点
  144. while(!stack.empty() && (stack.top())->tag == ‘R‘){
  145. BT = stack.top();
  146. //退栈
  147. stack.pop();
  148. BT->biTree;
  149. printf("%c ",BT->biTree->data);
  150. }
  151. //遍历右子树
  152. if(!stack.empty()){
  153. BT = stack.top();
  154. //访问过右子树
  155. BT->tag = ‘R‘;
  156. p = BT->biTree;
  157. p = p->rchild;
  158. }
  159. }//while
  160. }
  161. //层次遍历
  162. void LevelOrder(BiTree T){
  163. BiTree p = T;
  164. //队列
  165. queue<BiTree> queue;
  166. //根节点入队
  167. queue.push(p);
  168. //队列不空循环
  169. while(!queue.empty()){
  170. //对头元素出队
  171. p = queue.front();
  172. //访问p指向的结点
  173. printf("%c ",p->data);
  174. //退出队列
  175. queue.pop();
  176. //左子树不空,将左子树入队
  177. if(p->lchild != NULL){
  178. queue.push(p->lchild);
  179. }
  180. //右子树不空,将右子树入队
  181. if(p->rchild != NULL){
  182. queue.push(p->rchild);
  183. }
  184. }
  185. }
  186. int main()
  187. {
  188. BiTree T;
  189. CreateBiTree(T);
  190. printf("先序遍历:\n");
  191. PreOrder(T);
  192. printf("\n");
  193. printf("先序遍历(非递归):\n");
  194. PreOrder2(T);
  195. printf("\n");
  196. printf("中序遍历:\n");
  197. InOrder(T);
  198. printf("\n");
  199. printf("中序遍历(非递归):\n");
  200. InOrder2(T);
  201. printf("\n");
  202. printf("后序遍历:\n");
  203. PostOrder(T);
  204. printf("\n");
  205. printf("后序遍历(非递归):\n");
  206. PostOrder2(T);
  207. printf("\n");
  208. printf("层次遍历:\n");
  209. LevelOrder(T);
  210. printf("\n");
  211. return 0;
  212. }
时间: 2024-10-08 09:31:42

转 算法之二叉树各种遍历的相关文章

【转】算法之二叉树各种遍历

http://blog.csdn.net/sjf0115/article/details/8645991 树形结构是一类重要的非线性数据结构,其中以树和二叉树最为常用. 二叉树是每个结点最多有两个子树的有序树.通常子树的根被称作“左子树”(left subtree)和“右子树”(right subtree).二叉树常被用作二叉查找树和二叉堆或是二叉排序树.二叉树的每个结点至多只有二棵子树(不存在度大于2的结点),二叉树的子树有左右之分,次序不能颠倒.二叉树的第i层至多有2的 i -1次方个结点:

算法之二叉树各种遍历

树形结构是一类重要的非线性数据结构,当中以树和二叉树最为经常使用. 二叉树是每一个结点最多有两个子树的有序树.通常子树的根被称作"左子树"(left subtree)和"右子树"(right subtree).二叉树常被用作二叉查找树和二叉堆或是二叉排序树.二叉树的每一个结点至多仅仅有二棵子树(不存在度大于2的结点),二叉树的子树有左右之分,次序不能颠倒.二叉树的第i层至多有2的 i -1次方个结点:深度为k的二叉树至多有2^(k) -1个结点:对不论什么一棵二叉树

算法之二叉树各种遍历 (转)

树形结构是一类重要的非线性数据结构,其中以树和二叉树最为常用. 二叉树是每个结点最多有两个子树的有序树.通常子树的根被称作“左子树”(left subtree)和“右子树”(right subtree).二叉树常被用作二叉查找树和二叉堆或是二叉排序树.二叉树的每个结点至多只有二棵子树(不存在度大于2的结点),二叉树的子树有左右之分,次序不能颠倒.二叉树的第i层至多有2的 i -1次方个结点:深度为k的二叉树至多有2^(k) -1个结点:对任何一棵二叉树T,如果其终端结点数(即叶子结点数)为n0,

经典白话算法之二叉树各种遍历

树形结构是一类重要的非线性数据结构,其中以树和二叉树最为常用. 二叉树是每个结点最多有两个子树的有序树.通常子树的根被称作"左子树"(left subtree)和"右子树"(right subtree).二叉树常被用作二叉查找树和二叉堆或是二叉排序树.二叉树的每个结点至多只有二棵子树(不存在度大于2的结点),二叉树的子树有左右之分,次序不能颠倒.二叉树的第i层至多有2的 i -1次方个结点:深度为k的二叉树至多有2^(k) -1个结点:对任何一棵二叉树T,如果其终端

【数据结构与算法】二叉树深度遍历(递归)

二叉树的深度遍历用递归的话就没有什么好说的了. 代码实现 /** * 源码名称:TreeIteratorRecursion.java * 日期:2014-08-23 * 程序功能:二叉树深度遍历 * 版权:[email protected] * 作者:A2BGeek */ public class TreeIteratorRecursion { class TreeNode<T> { private T mNodeData; private TreeNode<T> mLeftChi

【数据结构与算法】二叉树广度遍历

二叉树的广度遍历想想还是比較简单的.利用队列存储当前结点的左儿子和右儿子用作未来的訪问. 代码实现 /** * 源代码名称:TreeBFS.java * 日期:2014-08-25 * 程序功能:二叉树广度遍历 * 版权:[email protected] * 作者:A2BGeek */ import java.util.LinkedList; import java.util.Queue; public class TreeBFS { class TreeNode<T> { private

【数据结构与算法】二叉树深度遍历(非递归)

据说这个笔试面试的时候非常easy考到,所以写到这里. 图示 代码实现 /** * 源代码名称:TreeIteratorNoRecursion.java * 日期:2014-08-23 * 程序功能:二叉树深度遍历(非递归) * 版权:[email protected] * 作者:A2BGeek */ import java.util.Stack; public class TreeIteratorNoRecursion { class TreeNode<T> { private T mNod

【每日算法】二叉树的遍历

二叉树特点 每个节点最多有两棵子树: 二叉树是有序的,即区分左右子树的次序. 完全二叉树 叶子节点只能出现在最下两层,且最下层的叶子节点都集中在二叉树左侧连续的位置. 如果有度为1的节点,只可能有一个,且该节点只有左孩子. 二叉树实现 这里只讲二叉链表实现,使用C++. template<class DataType> struct BiNode { DataType data; BiNode<DataType> *lchild, *rchild; }; template<c

数据结构与算法 3:二叉树,遍历,创建,释放,拷贝,求高度,面试,线索树

[本文谢绝转载,原文来自http://990487026.blog.51cto.com] 树 数据结构与算法 3:二叉树,遍历,创建,释放,拷贝,求高度,面试,线索树 二叉树的创建,关系建立 二叉树的创建,关系建立2 三叉链表法 双亲链表: 二叉树的遍历 遍历的分析PPT 计算二叉树中叶子节点的数目:使用全局变量计数器 计算二叉树中叶子节点的数目:不使用全局变量计数器 无论是先序遍历,中序遍历,后序遍历,求叶子的数字都不变;因为本质都是一样的,任何一个节点都会遍历3趟 求二叉树的高度 二叉树的拷