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. Springboot源码分析之TargetSource

    摘要: 其实我第一次看见这个东西的时候也是不解,代理目标源不就是一个class嘛还需要封装干嘛... 其实proxy代理的不是target,而是TargetSource,这点非常重要,一定要分清楚!! ...

  2. Oracle面对“数据倾斜列使用绑定变量”场景的解决方案

    1.背景知识介绍 2.构造测试用例 3.场景测试 4.总结 1.背景知识介绍     我们知道,Oracle在传统的OLTP(在线事务处理)类系统中,强烈推荐使用绑定变量,这样可以有效的减少硬解析从而 ...

  3. JQuery $.ajax(); 异步访问完整参数

    $.ajax 完整参数   jquery中的ajax方法参数 url: 要求为String类型的参数,(默认为当前页地址)发送请求的地址. type: 要求为String类型的参数,请求方式(post ...

  4. MySql基础架构以及SQL语句执行流程

    01. mysql基础架构 SQL语句是如何执行的 学习一下mysql的基础架构,从一条sql语句是如何执行的来学习. 一般我们写一条查询语句类似下面这样: select user,password ...

  5. Leetcode之分治法专题-169. 求众数(Majority Element)

    Leetcode之分治法专题-169. 求众数(Majority Element) 给定一个大小为 n 的数组,找到其中的众数.众数是指在数组中出现次数大于 ⌊ n/2 ⌋ 的元素. 你可以假设数组是 ...

  6. 2019icpc南京网络赛_F_Greedy Sequence

    题意 题意不明,队友告诉我对于每个\(i\),所在下标\(p[i]\),在\([p[i]-k,p[i]+k]\)中找到小于\(i\)的最大数\(x\),然后\(ans[i]=ans[x]+1\)即可. ...

  7. ROS中local costmap的原点坐标系

    local costmap是一个依赖于其他坐标系存在的坐标系统,它并不维护自己的坐标系,而是在另一个坐标系中设定坐标原点,然后记下自己的宽与高.它使用数据结构nav_msgs/OccupancyGri ...

  8. J-Subarray_2019牛客暑期多校训练营(第二场)

    题意 有一个只由1,-1组成的数组,给出所有连续的1所在位置,求满足1的个数大于-1的个数的子区间的数量 题解 参考博客:https://www.cnblogs.com/Yinku/p/1122149 ...

  9. gym/101873/GCPC2017

    题目链接:https://codeforces.com/gym/101873 C. Joyride 记忆化搜索形式的dp #include <algorithm> #include < ...

  10. CodeForces 821D Okabe and City

    Okabe and City 题解: 将行和列也视为一个点. 然后从普通的点走到行/列的点的话,就代表这行/列已经被点亮了. 然后将费用为0的点建上边. 注意讨论(n,m)非亮的情况下. 代码: #i ...