Nonrecursive Traversal of Binary Tree

First I wanna talk about why should we use <code>Stack</code> to implement this algorithm. I think it is due to the FILO feature of Stack, and that really matters and makes sense when you get around with tree stuff. Cause in nonrecursive way we have to remember where we came from, which means we must figure out how should we go back to the root node as we finish visiting the whole subtree. The key to understand and get good command of nonrecursive traversal of binary tree is to demostrate the whole process by your hand. Don't be lazy. :)

I. Preorder Nonrecursive Traversal

1.1 Algorithm

  In recursive way it is easy and simple to go back to the root when we finish handling or visiting the subtree. But it seems more tricky while you're using nonrecursive way, which means you need to find some auxiliary data structure to help you implement those algorithm. So there we go.

  In preorder way of traversing binary tree, we should visit the root first, then go to visit left subtree and visit the right subtree at last. So the most important thing is to bear the visiting order in mind.

  • a. visit the node and push it into stack as you go down along the left subtree untill it reaches the deepest bottom left node.
  • b. pop a node from the top of stack and check whether it owns a right node, and go right if does.
  • c. loop a ~ b until the stack is empty

  Noticing that when to visit the node may help you interpret this algorithm better.

1.2 Code

Talk is cheap, show me the code!    -- Linus Benedict Torvalds

 public class Node<E> {
public E data;
public Node<E> lnode;
public Node<E> rnode; public Node(){}
public Node(E data) {
this.data = data;
}
}
     /**
* preorder nonrecursive traversal with stack
* @param node
* @author sheepcore
*/
public void preOrderStk(Node<E> node) {
if(node == null)
return;
Node<E> root = node;
Stack<Node<E>> stack = new Stack<>();
while (root != null || !stack.isEmpty()){
while (root != null){
visit(root); //visit the root node first then go to left subtree
stack.push(root); //push left node into stack to go back
root = root.lnode;
}
if(!stack.isEmpty()){
root = stack.pop();
if(root.rnode != null) {
root = root.rnode; //visit the right subtree
} else {
root = null; //clear root when it is return from right subtree
}
}
}
}

II. Inorder Nonrecursive Traversal

2.1 Algorithm

  • a. push the node into stack as you go down along the left subtree untill it reaches the deepest bottom left node.
  • b. pop a node from the top of stack and visit it. Then check whether it owns a right node, go right if does.
  • c. loop a ~ b until the stack is empty

2.2 Code

     /**
* inorder non recursive traversal with stack
* @param node
* @author sheepcore
*/
public void inOrderStk(Node<E> node) {
if(node == null)
return;
Node<E> root = node;
Stack<Node<E>> stack = new Stack<>();
while (root != null || !stack.isEmpty()){
while (root != null){
stack.push(root); //go the left bottom of the tree
root = root.lnode;
}
if(!stack.isEmpty()){
root = stack.pop();
visit(root); //visit it
if(root.rnode != null) {
root = root.rnode;
} else {
root = null; //clear root when it is return from right subtree
}
}
}
}

III. Postorder Nonrecursive Traversal

3.1 Algorithm

  It is more tricky and tougher while traversing in postorder way than in preorder or inorder way. Cause we have no idea where it comes from when we go back to the root node. Therefore we got two common solutions: using tags or using two stacks.

  Using tags for each node can help us find out whether it comes from left subtree or right. Tag the node as "LEFT" while visiting the left subtree and change it to "RIGHT" as we finish. Then go right and go back the root and visit it.

  Using two stacks is prolly like this https://www.geeksforgeeks.org/iterative-postorder-traversal/ and check it out :).

3.2 Code

  1.Using tags

     /**
* postorder nonrecursive traversal of binary tree * The key to understand and get good command of this algorithm is to demonstrate the whole processes
* of each step and figure out how the nodes' status changed and for what that will make sense
* to you to permanently beat the algorithm. And you must know that each nodes should be pushed into
* stack twice which seems to be sort of time-consuming, so we can also check whether the nodes has
* right node when we go down alongside the left subtree and that could make the performance better. :)
* @param node
* @author sheepcore
*/
public void postOrderStk(Node<E> node){
if(root == null)
return;
Node<E> root = node;
stkNode<E> s;
Stack<stkNode<E>> stack = new Stack<>();
do {
while (root != null){ //go down along the left branches of the bitree
s = new stkNode<>();
s.node = root;
s.tag = tag.L;
stack.push(s);
root = root.lnode;
}//while-root
boolean continual = true; //flag for continue
while (continual && !stack.isEmpty()) {
s = stack.pop();
root = s.node;
switch (s.tag){
case L: s.tag = tag.R; //return from left subtree
stack.push(s);
continual = false;
root = root.rnode;
break;
case R: visit(root); break; //return from right subtree
}
}//while-continual-stack
}while (!stack.isEmpty());
}

  2.Using two stacks

     /**
* post-order nonrecursive traversal with two stacks
* @param node
*/
public void postOrderStk2(Node<E> node){
if(root == null)
return;
Node<E> root = node;
Stack<Node<E>> s1 = new Stack<>();
Stack<Node<E>> s2 = new Stack<>();
s1.push(root);
while (!s1.isEmpty()){
root = s1.pop();
s2.push(root);
if (root.lnode != null) { s1.push(root.lnode); }
if (root.rnode != null) { s1.push(root.rnode); }
}
while (!s2.isEmpty()){
visit(s2.pop());
}
System.out.println();
}

IV. Levelorder Nonrecursive Traversal

4.1 Algorithm

  In levelorder you need to visit the nodes from left to right and from top to bottom of the tree. Using a queue to implement the algorithm is way more simple as you think.

4.2 Code

    /**
* level order traversal using queue
* 1. check and visit the root node remove from the queue
* 2. add its left and right nodes if does
* 3. loop 1 ~ 2 while queue is empty
* @param root
* @author sheepcore
*/
public void levelOrder(Node<E> root){
if(root == null)
return;
LinkedList<Node<E>> q = new LinkedList<>();
Node<E> cur;
q.addLast(root);
while (!q.isEmpty()) {
cur = q.removeFirst();
visit(cur);
if(cur.lnode != null) {
q.addLast(cur.lnode);
}
if(cur.rnode != null){
q.addLast(cur.rnode);
}
}
}

V. Summary

  哈哈,自己的第一篇全英文博客,最近因为考试在练英文写作,所以干脆就趁热打铁一下。总结一下,这四种二叉树遍历的非递归方法其实总体逻辑不算难,需要自己多在纸上演算推导,感受推导逻辑之后,才能把它转化为代码逻辑,这样写出的代码会更加印象深刻的。其中后序非递归遍历稍微麻烦一点,需要考虑当回溯到根节点时,是从左子树来的呢?还是从右子树呢?因为这两种情况对根节点将执行不同的操作,所以需要特别处理。其实方法还有很多,这里自己只是简单实现了两种最常见的方法:标志位法和双堆栈法。好啦!我得滚去学习了,因为落后就要挨打了!!!

Sometimes people call you an idiot when you do something stupid, keep doing that until you change that stupid things to glory things. And then you can stare at those people's eyes and say, "I made it bitch!". 

数据结构之二叉树篇卷三 -- 二叉树非递归遍历(With Java)的更多相关文章

  1. 二叉树3种递归和非递归遍历(Java)

    import java.util.Stack; //二叉树3种递归和非递归遍历(Java) public class Traverse { /******************一二进制树的定义*** ...

  2. 数据结构二叉树的递归与非递归遍历之java,javascript,php实现可编译(1)java

    前一段时间,学习数据结构的各种算法,概念不难理解,只是被C++的指针给弄的犯糊涂,于是用java,web,javascript,分别去实现数据结构的各种算法. 二叉树的遍历,本分享只是以二叉树中的先序 ...

  3. C++学习---二叉树的输入及非递归遍历

    二叉树的二叉链表存储表示如下 //二叉树的二叉链表存储表示 typedef struct BiTNode { char data;//结点数据域 struct BiTNode* lchild, * r ...

  4. ZT 二叉树的非递归遍历

    ZT 二叉树的非递归遍历 二叉树的非递归遍历 二叉树是一种非常重要的数据结构,很多其它数据结构都是基于二叉树的基础演变而来的.对于二叉树,有前序.中序以及后序三种遍历方法.因为树的定义本身就 是递归定 ...

  5. Java实现二叉树的创建、递归/非递归遍历

    近期复习数据结构中的二叉树的相关问题,在这里整理一下 这里包含: 1.二叉树的先序创建 2.二叉树的递归先序遍历 3.二叉树的非递归先序遍历 4.二叉树的递归中序遍历 5.二叉树的非递归中序遍历 6. ...

  6. C++编程练习(17)----“二叉树非递归遍历的实现“

    二叉树的非递归遍历 最近看书上说道要掌握二叉树遍历的6种编写方式,之前只用递归方式编写过,这次就用非递归方式编写试一试. C++编程练习(8)----“二叉树的建立以及二叉树的三种遍历方式“(前序遍历 ...

  7. c/c++二叉树的创建与遍历(非递归遍历左右中,破坏树结构)

    二叉树的创建与遍历(非递归遍历左右中,破坏树结构) 创建 二叉树的递归3种遍历方式: 1,先中心,再左树,再右树 2,先左树,再中心,再右树 3,先左树,再右树,再中心 二叉树的非递归4种遍历方式: ...

  8. JAVA递归、非递归遍历二叉树(转)

    原文链接: JAVA递归.非递归遍历二叉树 import java.util.Stack; import java.util.HashMap; public class BinTree { priva ...

  9. 非递归遍历二叉树Java实现

    2018-10-03 20:16:53 非递归遍历二叉树是使用堆栈来进行保存,个人推荐使用双while结构,完全按照遍历顺序来进行堆栈的操作,当然在前序和后序的遍历过程中还有其他的压栈流程. 一.Bi ...

随机推荐

  1. redux 源码阅读

    目录 [目录结构] [utils] actionTypes.js isPlainObject.js warning.js [逻辑代码] index.js createStore.js compose. ...

  2. 人脸识别Demo

    ★.本实例使用百度智能云-人工智能-人脸识别API实现. ★.楼下安装了刷脸进门.闲暇时无聊写了个Demo 主界面显示如下图: 本实例,包括了所有人脸识别API的调用. 1. 创建楼号,对应API中创 ...

  3. 设计模式(C#)——07装饰者模式

    推荐阅读:  我的CSDN  我的博客园  QQ群:704621321       在一款战斗类的游戏中,随着故事情节的发展,玩家(即游戏中的主角,下文统一为主角)通常会解锁一些新技能.最初主角只有使 ...

  4. MSIL实用指南-位运算

    C#支持的位运算是与.或.异或.取反.左移.右移,它们对应的指令是And.Or.Xor.Not.Shl.Shr. 取反运算只需要一个操作数,生成步骤是1.生成加载变量2.生成取反指令实例代码: ilG ...

  5. 知识图谱推理与实践 (2) -- 基于jena实现规则推理

    本章,介绍 基于jena的规则引擎实现推理,并通过两个例子介绍如何coding实现. 规则引擎概述 jena包含了一个通用的规则推理机,可以在RDFS和OWL推理机使用,也可以单独使用. 推理机支持在 ...

  6. JIra配置权限方案

    目录: 添加用户 添加用户组 将用户分配到不同的组中 创建项目权限方案 配置项目采用的权限方案 1. 添加用户 1)使用admin权限的账户登录后,点击右上角的配置,选择system 2)在打开的页面 ...

  7. lightoj 1028 - Trailing Zeroes (I)(素数筛)

    We know what a base of a number is and what the properties are. For example, we use decimal number s ...

  8. HDU 1003 Max Sum * 最长递增子序列(求序列累加最大值)

    Max Sum Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total Sub ...

  9. Fractions Again?! UVA - 10976

    It is easy to see that for every fraction in the form 1k(k > 0), we can always find two positive ...

  10. Allegro PCB导入DXF文件详解

    一:导入方法 1.确认Allegro PCB的单位精度设置和DXF文件保持一致(一般情况下DXF文件用mm,Allegro文件用mil). 2. 在Allegro中点击File→Import→DXF… ...