二叉树三种遍历(递归与非递归)C++实现

ゝ一世哀愁。 2021-12-03 23:21 376阅读 0赞

文章目录

  • 遍历类型
  • 一个二叉树
  • 递归实现
  • 非递归实现
    • 先序遍历
    • 中序遍历
    • 后序遍历
  • 完整代码
  • 结果图

遍历类型

众所周知,二叉树的遍历有三种:前序遍历、中序遍历和后序遍历。三者不同之处是访问节点的先后次序(注意该先后次序是指节点、节点左子树、节点右子树,这三部分的顺序而非每一个节点的顺序,这三部分内部的遍历顺序与外部保持一致)。三种遍历的实现依靠递归或者非递归两种。




















遍历类型 遍历方式
前序遍历 中左右
中序遍历 左中右
后序遍历 左右中

所以,前中后三种遍历是指节点相对于其左孩子和右孩子的位置。

一个二叉树

如下图一个满的完全二叉树

1

2

3

4

5

6

7

递归实现

遍历时,遇空则返回上一级,使用下图更直接些。

1

2

3

4

5

6

7

null

null

null

null

null

null

null

null

因为4/5/6/7四个节点的左右孩子为空,所以到达空节点后会返回节点,拿节点4为例:

先遍历其左孩子发现为null,返回到4的位置

然后遍历其右孩子发现还为null返回4

4

4

4

4的树遍历完后返回到2的位置,开始进行2的右孩子的遍历。其他节点同理。
遍历的实际过程为:124442555213666377731
神奇的事情发生了!

  1. 先序遍历实际就是打印数值第一次出现时间,即1245367
  2. 中序遍历实际为打印数值第二次出现时间,即4251637
  3. 后序遍历实际为打印数值第三次出现时间,即4526731

观察代码你会发现,三种遍历方法的不同之处就是打印操作出现的时间!

  1. 先序遍历(中左右):打印放前 → \rightarrow →递归左子树 → \rightarrow →递归右子树
  2. 中序遍历(左中右):递归左子树 → \rightarrow →打印放中 → \rightarrow →递归右子树
  3. 后序遍历(左右中):递归左子树 → \rightarrow →递归右子树 → \rightarrow →打印放后
    所以,递归对于二叉树是好用的,他可以实访问一个节点三次。

非递归实现

用非递归实现二叉树时,要借用堆(stack)这个数据结构。因为递归的实质就是系统压栈出栈的过程,不使用递归则要申请一个额外的空间使用栈来实现二叉树的遍历(用队列好像也可以)。强烈建议自己动手画一遍过程!!!

先序遍历

使用一个栈stack。
先将头结点压入,再在栈不为空的前提下循环:
1.弹出栈顶元素
2.判断该节点右孩子不为空,压入该右孩子
3.判断该节点左孩子不为空,压入该左孩子(此时该值为栈顶元素),循环1的操作
故,第一步把头结点弹出,然后先压右再压左,弹出的顺序便是先弹左再弹右,符合中左右的顺序。

中序遍历

使用一个栈。
在该节点不为空或栈不为空的前提下循环:
1.节点不为空,将该节点与其左边界(左孩子的左孩子的左…)全部顺次压入栈中,直至为空
2.节点为空,则将栈顶元素(左边界值)弹出(此时栈顶是其父节点),移至右孩子,判空后执行响应操作。

后序遍历

使用两个栈。(也可以使用一个栈)
一个栈temp用来临时存放,另一个栈用来弹出遍历结果。
先将头结点压入temp中:
1.栈temp不空,则把栈顶元素弹出当做当前节点,并压入另一个栈中,若该节点左右节点为空,则一直进行栈顶元素的弹出和压入,否则先压其左孩子再压其右孩子,直至栈空为止。
2.当要弹出遍历结果的栈不空时,一次弹出栈顶元素即为遍历结果。
一开始就把头结点压入了栈中,在temp中先压左再压右的目的就是弹出给stackpull中是以先右后左的顺序。这样就做到了弹出时按照左右中的顺序。

完整代码

  1. #include<iostream>
  2. #include<stack>
  3. using namespace std;
  4. struct Node
  5. {
  6. int data;
  7. Node *left;
  8. Node *right;
  9. Node(int data)
  10. {
  11. this->data = data;
  12. this->left = NULL;
  13. this->right = NULL;
  14. }
  15. };
  16. class BinTree
  17. {
  18. public:
  19. Node *root;
  20. Node *CreateTree();
  21. void Preorder(Node *root);
  22. void Inorder(Node *root);
  23. void Postorder(Node *root);
  24. void Preorder2(Node *root);
  25. void Inorder2(Node *root);
  26. void Postorder2(Node *root);
  27. };
  28. Node* BinTree::CreateTree()
  29. {
  30. Node *p1 = new Node(1);
  31. Node *p2 = new Node(2);
  32. Node *p3 = new Node(3);
  33. Node *p4 = new Node(4);
  34. Node *p5 = new Node(5);
  35. Node *p6 = new Node(6);
  36. Node *p7 = new Node(7);
  37. p1->left = p2; p1->right = p3;
  38. p2->left = p4; p2->right = p5;
  39. p3->left = p6; p3->right = p7;
  40. root = p1;
  41. return root;
  42. }
  43. /*二叉树递归遍历*/
  44. void BinTree::Preorder(Node *root)
  45. {
  46. if (root == NULL) return;
  47. else
  48. {
  49. cout << root->data << "";
  50. Preorder(root->left);
  51. Preorder(root->right);
  52. }
  53. }
  54. void BinTree::Inorder(Node *root)
  55. {
  56. if (root == NULL) return;
  57. else
  58. {
  59. Inorder(root->left);
  60. cout << root->data << "";
  61. Inorder(root->right);
  62. }
  63. }
  64. void BinTree::Postorder(Node *root)
  65. {
  66. if (root == NULL) return;
  67. else
  68. {
  69. Postorder(root->left);
  70. Postorder(root->right);
  71. cout << root->data << "";
  72. }
  73. }
  74. /*二叉树非递归遍历*/
  75. void BinTree::Preorder2(Node *root)
  76. {
  77. if (root == NULL) return;
  78. else
  79. {
  80. stack<Node*> stack;
  81. stack.push(root);
  82. while (!stack.empty())
  83. {
  84. root = stack.top();
  85. cout << root->data << "";
  86. stack.pop();//stack.top()可以给root而stack.pop()却不能??
  87. if (root->right != NULL)
  88. stack.push(root->right);
  89. if (root->left != NULL)
  90. stack.push(root->left);
  91. }
  92. }
  93. cout << endl;
  94. }
  95. void BinTree::Inorder2(Node *root)
  96. {
  97. if (root == NULL) return;
  98. else
  99. {
  100. stack<Node*> stack;
  101. while (!stack.empty() || root != NULL)
  102. {
  103. if (root != NULL)
  104. {
  105. stack.push(root);
  106. root = root->left;
  107. }
  108. else
  109. {
  110. root = stack.top();
  111. cout << root->data << "";
  112. stack.pop();
  113. root = root->right;
  114. }
  115. }
  116. }
  117. cout << endl;
  118. }
  119. void BinTree::Postorder2(Node *root)
  120. {
  121. if (root == NULL) return;
  122. else
  123. {
  124. stack<Node*> stacktemp;
  125. stack<Node*> stackpull;
  126. stacktemp.push(root);
  127. while (!stacktemp.empty())
  128. {
  129. root = stacktemp.top();
  130. stacktemp.pop();
  131. stackpull.push(root);
  132. if (root->left != NULL)
  133. stacktemp.push(root->left);
  134. if (root->right != NULL)
  135. stacktemp.push(root->right);
  136. }
  137. while (!stackpull.empty())
  138. {
  139. root = stackpull.top();
  140. cout << root->data << "";
  141. stackpull.pop();
  142. }
  143. }
  144. cout << endl;
  145. }
  146. int main()
  147. {
  148. BinTree tree;
  149. tree.CreateTree();
  150. cout << "********二叉树递归遍历********";
  151. cout << endl;
  152. cout << "先序递归结果:";
  153. tree.Preorder(tree.root);
  154. cout << endl;
  155. cout << "中序递归结果:";
  156. tree.Inorder(tree.root);
  157. cout << endl;
  158. cout << "后序递归结果:";
  159. tree.Postorder(tree.root);
  160. cout << endl;
  161. cout << "********二叉树非递归遍历********";
  162. cout << endl;
  163. cout << "先序非递归结果:";
  164. tree.Preorder2(tree.root);
  165. cout << "中序非递归结果:";
  166. tree.Inorder2(tree.root);
  167. cout << "后序非递归结果:";
  168. tree.Postorder2(tree.root);
  169. return 0;
  170. }

结果图

遍历结果图

发表评论

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

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

相关阅读

    相关 ()——

    1、前序遍历 根据前序遍历访问的顺序,优先访问根结点,然后再分别访问左孩子和右孩子。即对于任一结点,其可看做是根结点,因此可以直接访问,访问完之后,若其左孩子不为空,按相同

    相关 实现

    1.二叉树前序遍历的非递归实现 \ 实现思路,先序遍历是要先访问根节点,然后再去访问左子树以及右子树,这明显是递归定义,但这里是用栈来实现的 \ 首先需要先从栈顶取出