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

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

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

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

[cpp] view
plain
copy

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

二叉树的创建:

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

[cpp] view
plain
copy

  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
plain
copy

  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
plain
copy

  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
plain
copy

  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
plain
copy

  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
plain
copy

  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
plain
copy

  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-29 19:11:20

经典白话算法之二叉树各种遍历的相关文章

经典白话算法之二叉树中序前序序列(或后序)求解树

这种题一般有二种形式,共同点是都已知中序序列.如果没有中序序列,是无法唯一确定一棵树的. <1>已知二叉树的前序序列和中序序列,求解树. 1.确定树的根节点.树根是当前树中所有元素在前序遍历中最先出现的元素. 2.求解树的子树.找出根节点在中序遍历中的位置,根左边的所有元素就是左子树,根右边的所有元素就是右子树.若根节点左边或右边为空,则该方向子树为空:若根节点 边和右边都为空,则根节点已经为叶子节点. 3.递归求解树.将左子树和右子树分别看成一棵二叉树,重复1.2.3步,直到所有的节点完成定

经典白话算法之中缀表达式和后缀表达式

一.后缀表达式求值 后缀表达式也叫逆波兰表达式,其求值过程可以用到栈来辅助存储. 假定待求值的后缀表达式为:6  5  2  3  + 8 * + 3  +  *,则其求值过程如下: (1)遍历表达式,遇到的数字首先放入栈中,依次读入6 5 2 3 此时栈如下所示: (2)接着读到"+",则从栈中弹出3和2,执行3+2,计算结果等于5,并将5压入到栈中. (3)然后读到8(数字入栈),将其直接放入栈中. (4)读到"*",弹出8和5,执行8*5,并将结果40压入栈中

经典白话算法之归并排序

void Merge(int A[],int p,int q,int r){ int i,j,k; //计算子数组A[p..q]的元素个数 int n1 = q - p + 1; //计算子数组A[q+1..r]元素个数 int n2 = r - q; //创建子数组L,R int* L = (int*)malloc(sizeof(int)*(n1+1)); int* R = (int*)malloc(sizeof(int)*(n2+1)); //将子数组A[p..q]赋值到L数组 for(i

经典白话算法之快速排序

[分析] [伪代码] [运行过程] [代码] /********************************* * 日期:2014-04-01 * 作者:SJF0115 * 题目:快速排序 **********************************/ #include <iostream> #include <stdio.h> using namespace std; //对子数组array[p...r]就地重排 int Partition(int array[],i

经典白话算法之优先级队列

<1>概念 优先级队列,顾名思义,就是一种根据一定优先级存储和取出数据的队列.它可以说是队列和排序的完美结合体,不仅可以存储数据,还可以将这些数据按照我们设定的规则进行排序.优先级队列是堆的一种常见应用.有最大优先级队列(最大堆)和最小优先级队列(最小堆).优先级队列是一种维护有一组元素构成的集合S的数据结构. <2>优先队列支持的基本运算 [cpp] view plaincopy //建立一个保存元素为int的优先级队列,其实是建了一个小顶堆 //但是请特别注意这样的建的堆默认是

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

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,

转 算法之二叉树各种遍历

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