【Java】 大话数据结构(9) 树(二叉树、线索二叉树)一文中,已经实现了采用递归方法的前、中、后序遍历,本文补充了采用循环的实现方法、以及层序遍历并进行了一个总结。

递归实现

  1. /*
  2. * 前序遍历
  3. */
  4. public void preOrder() {
  5. preOrderTraverse(root);
  6. System.out.println();
  7. }
  8. private void preOrderTraverse(BiTNode<E> node) {
  9. if(node==null)
  10. return;
  11. System.out.print(node.data);
  12. preOrderTraverse(node.lchild);
  13. preOrderTraverse(node.rchild);
  14. }
  15.  
  16. /*
  17. * 中序遍历
  18. */
  19. public void inOrder() {
  20. inOrderTraverse(root);
  21. System.out.println();
  22. }
  23. private void inOrderTraverse(BiTNode<E> node) {
  24. if(node==null)
  25. return;
  26. inOrderTraverse(node.lchild);
  27. System.out.print(node.data);
  28. inOrderTraverse(node.rchild);
  29. }
  30.  
  31. /*
  32. * 后序遍历
  33. */
  34. public void postOrder() {
  35. postOrderTraverse(root);
  36. System.out.println();
  37. }
  38. private void postOrderTraverse(BiTNode<E> node) {
  39. if(node==null)
  40. return;
  41. postOrderTraverse(node.lchild);
  42. postOrderTraverse(node.rchild);
  43. System.out.print(node.data);
  44. }

非递归实现(迭代)

   非递归实现,需要先创建一个栈,利用其特性来进行储存和输出。

  • 前序遍历,先输出当前点的值,一直沿着左子树进行读取,没左子树就在右子树重复上述过程。
  • 中序遍历与前序遍历基本一致,只是输出值的代码位置不同。
  • 后序遍历由于要左右子树输出完后才能输出根结点,所以增加一个栈进行标记是否完成左右子树的输出,其余思想基本类似。

  下面代码中,要注意node的结点位置和stack.peek()的位置关系。

  此外,在后序非递归遍历的过程中,栈中保留的是当前结点的所有祖先。这是和先序及中序遍历不同的。在某些和祖先有关的算法中,此算法很有价值。

  1. /**
  2. * 前序遍历(非递归)
  3. */
  4. public void preOrder2() {
  5. preOrder2(root);
  6. System.out.println();
  7. }
  8. private void preOrder2(BiTNode node) {
  9. Stack<BiTNode> stack = new Stack<BiTNode>();
  10. while(node!=null||!stack.isEmpty()) {
  11. while(node!=null) {
  12. System.out.print(node.data);
  13. stack.push(node);
  14. node=node.lchild;
  15. }
  16. node=stack.pop().rchild;
  17. }
  18. }
  19.  
  20. /**
  21. * 中序遍历
  22. */
  23. public void inOrder2() {
  24. inOrder2(root);
  25. System.out.println();
  26. }
  27. private void inOrder2(BiTNode node) {
  28. Stack<BiTNode> stack = new Stack<BiTNode>();
  29. while(node!=null||!stack.isEmpty()) {
  30. while(node!=null) {
  31. stack.push(node);
  32. node=node.lchild;
  33. }
  34. node=stack.pop();
  35. System.out.print(node.data);
  36. node=node.rchild;
  37. }
  38. }
  39.  
  40. /**
  41. * 后序遍历
  42. */
  43. public void postOrder2() {
  44. postOrder2(root);
  45. System.out.println();
  46. }
  47. private void postOrder2(BiTNode node) {
  48. Stack<BiTNode> stack = new Stack<BiTNode>();
  49. Stack<Integer> tag = new Stack<Integer>();
    //下面这段注释也能实现,与后面未注释部分基本一致。代表了自己的思考过程,就不删除了
  50. // while(node!=null||!stack.isEmpty()) {
  51. // while(node!=null){
  52. // stack.push(node);
  53. // tag.push(0);
  54. // node=node.lchild;
  55. // }
  56. //注释中的tag用于标记当前结点是否完成左右子结点遍历(所以用0,1表示)
  57. // while(!tag.isEmpty()&&tag.peek()==1) { //栈顶节点的左右子结点已完成遍历
  58. // System.out.print(stack.pop().data);
  59. // tag.pop();
  60. // }
  61. // if(!tag.isEmpty()) { //上面和这里的 !flag.isEmpty() 不可省略,不然会出错。
  62. // tag.pop();
  63. // tag.push(1);
  64. // node=stack.peek().rchild;
  65. // }
  66. // }
  67. /*后序遍历时,分别从左子树和右子树共两次返回根结点(用tag表示次数),
  68. * 只有从右子树返回时才访问根结点,所以增加一个栈标记到达结点的次序。
  69. */
  70. while(node!=null||!stack.isEmpty()) {
  71. if(node!=null){
  72. stack.push(node);
  73. tag.push(1); //第一次访问
  74. node=node.lchild;
  75. }else {
  76. if(tag.peek()==2) {
  77. System.out.print(stack.pop().data);
  78. tag.pop();
  79. }else {
  80. tag.pop();
  81. tag.push(2); //第二次访问
  82. node=stack.peek().rchild;
  83. }
  84. }
  85. }
  86. }

  

20191104:前序和后序的非递归遍历还可以合理利用栈的性质来实现,与上面的稍有不同。

前序:

  1. public List<Integer> preorderTraversal(TreeNode root) {
  2. ArrayList<Integer> list = new ArrayList<Integer>();
  3. Stack<TreeNode> stk = new Stack<>();
  4. stk.push(root);
  5. while(!stk.isEmpty()){
  6. TreeNode node = stk.pop();
  7. if(node==null)
  8. continue;
  9. list.add(node.val);
  10. stk.push(node.right);
  11. stk.push(node.left);
  12. }
  13. return list;
  14. }

后序:

  1. public List<Integer> postorderTraversal(TreeNode root) {
  2. LinkedList<Integer> list = new LinkedList<Integer>();
  3. Stack<TreeNode> stk = new Stack<>();
  4. stk.push(root);
  5. while(!stk.isEmpty()){
  6. TreeNode node = stk.pop();
  7. if(node==null)
  8. continue;
  9. list.addFirst(node.val); //LinkedList's method. If using ArrayList here,then using 'Collections.reverse(list)'' in the end;
  10. stk.push(node.left);
  11. stk.push(node.right);
  12. }
  13. return list;
  14. }

  

层序遍历

  合理利用队列的性质即可。

  1. public void levelOrder() {
  2. BiTNode<E> node =root;
  3. LinkedList<BiTNode<E>> list = new LinkedList<>();
  4. list.add(node);
  5. while(!list.isEmpty()) {
  6. node=list.poll();
  7. System.out.print(node.data);
  8. if(node.lchild!=null)
  9. list.offer(node.lchild);
  10. if(node.rchild!=null)
  11. list.offer(node.rchild);
  12. }
  13. }

  

完整代码(含测试代码)

  1. package BiTree;
  2.  
  3. import java.util.LinkedList;
  4. import java.util.Stack;
  5.  
  6. class BiTNode<E>{
  7. E data;
  8. BiTNode<E> lchild,rchild;
  9. public BiTNode(E data) {
  10. this.data=data;
  11. this.lchild=null;
  12. this.rchild=null;
  13. }
  14. }
  15.  
  16. public class BiTree<E> {
  17.  
  18. private BiTNode<E> root;
  19.  
  20. public BiTree() {
  21. //root=new BiTNode(null, null, null);
  22. root=null;
  23. }
  24.  
  25. /*
  26. * 前序遍历
  27. */
  28. public void preOrder() {
  29. preOrderTraverse(root);
  30. System.out.println();
  31. }
  32. private void preOrderTraverse(BiTNode<E> node) {
  33. if(node==null)
  34. return;
  35. System.out.print(node.data);
  36. preOrderTraverse(node.lchild);
  37. preOrderTraverse(node.rchild);
  38. }
  39.  
  40. /*
  41. * 中序遍历
  42. */
  43. public void inOrder() {
  44. inOrderTraverse(root);
  45. System.out.println();
  46. }
  47. private void inOrderTraverse(BiTNode<E> node) {
  48. if(node==null)
  49. return;
  50. inOrderTraverse(node.lchild);
  51. System.out.print(node.data);
  52. inOrderTraverse(node.rchild);
  53. }
  54.  
  55. /*
  56. * 后序遍历
  57. */
  58. public void postOrder() {
  59. postOrderTraverse(root);
  60. System.out.println();
  61. }
  62. private void postOrderTraverse(BiTNode<E> node) {
  63. if(node==null)
  64. return;
  65. postOrderTraverse(node.lchild);
  66. postOrderTraverse(node.rchild);
  67. System.out.print(node.data);
  68. }
  69.  
  70. //===============循环遍历===============
  71. /**
  72. * 前序遍历(非递归)
  73. */
  74. public void preOrder2() {
  75. preOrder2(root);
  76. System.out.println();
  77. }
  78. private void preOrder2(BiTNode node) {
  79. Stack<BiTNode> stack = new Stack<BiTNode>();
  80. while(node!=null||!stack.isEmpty()) {
  81. while(node!=null) {
  82. System.out.print(node.data);
  83. stack.push(node);
  84. node=node.lchild;
  85. }
  86. node=stack.pop().rchild;
  87. }
  88. }
  89.  
  90. /**
  91. * 中序遍历
  92. */
  93. public void inOrder2() {
  94. inOrder2(root);
  95. System.out.println();
  96. }
  97. private void inOrder2(BiTNode node) {
  98. Stack<BiTNode> stack = new Stack<BiTNode>();
  99. while(node!=null||!stack.isEmpty()) {
  100. while(node!=null) {
  101. stack.push(node);
  102. node=node.lchild;
  103. }
  104. node=stack.pop();
  105. System.out.print(node.data);
  106. node=node.rchild;
  107. }
  108. }
  109.  
  110. /**
  111. * 后序遍历
  112. */
  113. public void postOrder2() {
  114. postOrder2(root);
  115. System.out.println();
  116. }
  117. private void postOrder2(BiTNode node) {
  118. Stack<BiTNode> stack = new Stack<BiTNode>();
  119. Stack<Integer> tag = new Stack<Integer>();
  120. // while(node!=null||!stack.isEmpty()) {
  121. // while(node!=null){
  122. // stack.push(node);
  123. // tag.push(0);
  124. // node=node.lchild;
  125. // }
  126. //这里的tag用于标记当前结点是否完成左右子结点遍历(所以用0,1表示)
  127. // while(!tag.isEmpty()&&tag.peek()==1) { //栈顶节点的左右子结点已完成遍历
  128. // System.out.print(stack.pop().data);
  129. // tag.pop();
  130. // }
  131. // if(!tag.isEmpty()) { //上面和这里的 !flag.isEmpty() 不可省略,不然会出错。
  132. // tag.pop();
  133. // tag.push(1);
  134. // node=stack.peek().rchild;
  135. // }
  136. // }
  137. /*后序遍历时,分别从左子树和右子树共两次返回根结点(用tag表示次数),
  138. * 只有从右子树返回时才访问根结点,所以增加一个栈标记到达结点的次序。
  139. */
  140. while(node!=null||!stack.isEmpty()) {
  141. if(node!=null){
  142. stack.push(node);
  143. tag.push(1); //第一次访问
  144. node=node.lchild;
  145. }else {
  146. if(tag.peek()==2) {
  147. System.out.print(stack.pop().data);
  148.  
  149. tag.pop();
  150. }else {
  151. tag.pop();
  152. tag.push(2); //第二次访问
  153. node=stack.peek().rchild;
  154. }
  155. }
  156. }
  157. }
  158.  
  159. //=========层序遍历============
  160. public void levelOrder() {
  161. BiTNode<E> node =root;
  162. LinkedList<BiTNode<E>> list = new LinkedList<>();
  163. list.add(node);
  164. while(!list.isEmpty()) {
  165. node=list.poll();
  166. System.out.print(node.data);
  167. if(node.lchild!=null)
  168. list.offer(node.lchild);
  169. if(node.rchild!=null)
  170. list.offer(node.rchild);
  171. }
  172. }
  173.  
  174. public static void main(String[] args) {
  175. BiTree<String> aBiTree = new BiTree<String>();
  176. aBiTree.root=new BiTNode<String>("A");
  177. aBiTree.root.lchild=new BiTNode<String>("B");
  178. aBiTree.root.rchild=new BiTNode<String>("C");
  179. aBiTree.root.lchild.rchild=new BiTNode<String>("D");
  180.  
  181. // BiTree<String> aBiTree = new BiTree<String>();
  182. // aBiTree.root=new BiTNode("A");
  183. // aBiTree.root.lchild=new BiTNode("B");
  184. // aBiTree.root.lchild.lchild=new BiTNode("C");
  185. // aBiTree.root.lchild.lchild.lchild=new BiTNode("D");
  186. // aBiTree.root.lchild.rchild=new BiTNode("E");
  187. // aBiTree.root.lchild.rchild.lchild=new BiTNode("F");
  188. // aBiTree.root.lchild.rchild.lchild.rchild=new BiTNode("G");
  189. // aBiTree.root.lchild.rchild.lchild.rchild.rchild=new BiTNode("H");
  190.  
  191. System.out.println("————前序————");
  192. aBiTree.preOrder();
  193. aBiTree.preOrder2();
  194. System.out.println("————中序————");
  195. aBiTree.inOrder();
  196. aBiTree.inOrder2();
  197. System.out.println("————后序————");
  198. aBiTree.postOrder();
  199. aBiTree.postOrder2();
  200. System.out.println("————层序遍历————");
  201. aBiTree.levelOrder();
  202. }
  203. }

 

  1. ————前序————
  2. ABDC
  3. ABDC
  4. ————中序————
  5. BDAC
  6. BDAC
  7. ————后序————
  8. DBCA
  9. DBCA
  10. ————层序遍历————
  11. ABCD

遍历结果

参考:常用数据结构算法:二叉树的遍历(递归和非递归)

【Java】 二叉树的遍历(递归与循环+层序遍历)的更多相关文章

  1. 二叉树(前序,中序,后序,层序)遍历递归与循环的python实现

    二叉树的遍历是在面试使比较常见的项目了.对于二叉树的前中后层序遍历,每种遍历都可以递归和循环两种实现方法,且每种遍历的递归实现都比循环实现要简洁.下面做一个小结. 一.中序遍历 前中后序三种遍历方法对 ...

  2. Java实现 LeetCode 429 N叉树的层序遍历

    429. N叉树的层序遍历 给定一个 N 叉树,返回其节点值的层序遍历. (即从左到右,逐层遍历). 例如,给定一个 3叉树 : 返回其层序遍历: [ [1], [3,2,4], [5,6] ] 说明 ...

  3. Java二叉树实现及递归与非递归遍历实现

    树的遍历分两种:1.深度优先遍历 1.1 递归算法实现 2.2 非递归算法实现(使用栈存储)2.广度优先遍历(使用队列存储) import java.util.*; /** * 类功能描述: 二叉树遍 ...

  4. 【LeetCode-面试算法经典-Java实现】【107-Binary Tree Level Order Traversal II(二叉树层序遍历II)】

    [107-Binary Tree Level Order Traversal II(二叉树层序遍历II)] [LeetCode-面试算法经典-Java实现][全部题目文件夹索引] 原题 Given a ...

  5. c++智能指针和二叉树(1): 图解层序遍历和逐层打印二叉树

    二叉树是极为常见的数据结构,关于如何遍历其中元素的文章更是数不胜数. 然而大多数文章都是讲解的前序/中序/后序遍历,有关逐层打印元素的文章并不多,已有文章的讲解也较为晦涩读起来不得要领.本文将用形象的 ...

  6. 二叉树的层序遍历(levelordertraverse)

    数据结构关于二叉树的遍历还有一种层序遍历,按层次依次输出元素.最上层最先输出,同层中最左最先输出,使用队列这一结构来实现: int levelOrderTraverse(IDTree *pTree) ...

  7. Java 二叉树遍历相关操作

    BST二叉搜索树节点定义: /** * BST树的节点类型 * @param <T> */ class BSTNode<T extends Comparable<T>&g ...

  8. PTA 树的遍历(根据后序中序遍历输出层序遍历)

      给定一棵二叉树的后序遍历和中序遍历,请你输出其层序遍历的序列.这里假设键值都是互不相等的正整数. 输入格式:输入第一行给出一个正整数N(≤30),是二叉树中结点的个数.第二行给出其后序遍历序列.第 ...

  9. L2-006 树的遍历 (层序遍历)

    根据访问根节点与左右子树的先后顺序,二叉树一般有三种遍历方式:先序遍历.中序遍历和后序遍历. 只要给定中序遍历序列与先序或后序中的一种,可以还原二叉树结构.学习数据结构课程时,一直都只会手动构建还原二 ...

随机推荐

  1. 【bzoj1030】 JSOI2007—文本生成器

    http://www.lydsy.com/JudgeOnline/problem.php?id=1030 (题目链接) 题意 给出$n$个单词,问有多少个长度为$m$的文本中至少包含一个单词. Sol ...

  2. 【HDU5687】Trie

    题目大意:需要维护一个支持以下操作的数据结构:(1)支持插入一个字符串(2)支持删除所有前缀等于给定字符串的单词(3)查询该数据结构中是否存在一个以给定字符串为前缀的字符串 题解:由题目可知,需要维护 ...

  3. jQuery EasyUI API 中文文档 - 消息框(Messager)

    http://www.cnblogs.com/Philoo/archive/2011/11/15/jeasyui_api_messager.html Messager  消息框 博客园 风流涕淌 (p ...

  4. Controller、Service、Dao进行Junit单元

    原文链接:http://blog.csdn.net/u013041642/article/details/71430293 Spring对Controller.Service.Dao进行Junit单元 ...

  5. SQL Server 性能优化详解

    故事开篇:你和你的团队经过不懈努力,终于使网站成功上线,刚开始时,注册用户较少,网站性能表现不错,但随着注册用户的增多,访问速度开始变慢,一些用户开始发来邮件表示抗议,事情变得越来越糟,为了留住用户, ...

  6. 位运算符和unity Layers

    按位运算符:与(&).非(~).或(|).异或(^).<<(左移).>>(右移).位运算符主要用来对二进制位进行操作. 逻辑运算符:&&.||.!.逻辑 ...

  7. bzoj千题计划236:bzoj2300: [HAOI2011]防线修建

    http://www.lydsy.com/JudgeOnline/problem.php?id=2300 维护动态凸包,人懒用的set 用叉积判断,不要用斜率 #include<set> ...

  8. 数学:拓展Lucas定理

    拓展Lucas定理解决大组合数取模并且模数为任意数的情况 大概的思路是把模数用唯一分解定理拆开之后然后去做 然后要解决的一个子问题是求模质数的k次方 将分母部分转化成逆元再去做就好了 这里贴一份别人的 ...

  9. 关于MYSQL group by 分组按时间取最大值的实现方法

    类如 有一个帖子的回复表,posts( id , tid , subject , message ,  dateline ) , id 为 自动增长字段, tid为该回复的主题帖子的id(外键关联), ...

  10. 第6月第19天 lua动态链接库(luaopen_*函数的使用) skynet

    1. 给这个测试库取名为dylib,它包含一个函数add.lua中这样使用: local dylib = require "dylib.test"    local c = dyl ...