数据结构之二叉树篇卷三 -- 二叉树非递归遍历(With Java)
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)的更多相关文章
- 二叉树3种递归和非递归遍历(Java)
import java.util.Stack; //二叉树3种递归和非递归遍历(Java) public class Traverse { /******************一二进制树的定义*** ...
- 数据结构二叉树的递归与非递归遍历之java,javascript,php实现可编译(1)java
前一段时间,学习数据结构的各种算法,概念不难理解,只是被C++的指针给弄的犯糊涂,于是用java,web,javascript,分别去实现数据结构的各种算法. 二叉树的遍历,本分享只是以二叉树中的先序 ...
- C++学习---二叉树的输入及非递归遍历
二叉树的二叉链表存储表示如下 //二叉树的二叉链表存储表示 typedef struct BiTNode { char data;//结点数据域 struct BiTNode* lchild, * r ...
- ZT 二叉树的非递归遍历
ZT 二叉树的非递归遍历 二叉树的非递归遍历 二叉树是一种非常重要的数据结构,很多其它数据结构都是基于二叉树的基础演变而来的.对于二叉树,有前序.中序以及后序三种遍历方法.因为树的定义本身就 是递归定 ...
- Java实现二叉树的创建、递归/非递归遍历
近期复习数据结构中的二叉树的相关问题,在这里整理一下 这里包含: 1.二叉树的先序创建 2.二叉树的递归先序遍历 3.二叉树的非递归先序遍历 4.二叉树的递归中序遍历 5.二叉树的非递归中序遍历 6. ...
- C++编程练习(17)----“二叉树非递归遍历的实现“
二叉树的非递归遍历 最近看书上说道要掌握二叉树遍历的6种编写方式,之前只用递归方式编写过,这次就用非递归方式编写试一试. C++编程练习(8)----“二叉树的建立以及二叉树的三种遍历方式“(前序遍历 ...
- c/c++二叉树的创建与遍历(非递归遍历左右中,破坏树结构)
二叉树的创建与遍历(非递归遍历左右中,破坏树结构) 创建 二叉树的递归3种遍历方式: 1,先中心,再左树,再右树 2,先左树,再中心,再右树 3,先左树,再右树,再中心 二叉树的非递归4种遍历方式: ...
- JAVA递归、非递归遍历二叉树(转)
原文链接: JAVA递归.非递归遍历二叉树 import java.util.Stack; import java.util.HashMap; public class BinTree { priva ...
- 非递归遍历二叉树Java实现
2018-10-03 20:16:53 非递归遍历二叉树是使用堆栈来进行保存,个人推荐使用双while结构,完全按照遍历顺序来进行堆栈的操作,当然在前序和后序的遍历过程中还有其他的压栈流程. 一.Bi ...
随机推荐
- Flink中watermark为什么选择最小一条(源码分析)
昨天在社区群看到有人问,为什么水印取最小的一条?这里分享一下自己的理解 首先水印一般是设置为:(事件时间 - 指定的值) 这里的作用是解决迟到数据的问题,从源码来看一下它如何解决的 先来看下wind ...
- Python笔记_基础
1.注释 # 单行注释 """ 多行注释,一般用于类说明 """ 或 ''' 多行注释 ''' 2.工作日志 # TODO 说明性文字 记录 ...
- Docker学习总结(四)--应用部署
MySQL部署 1) 拉取 mysql 镜像 docker pull centos/mysql:5.7 2) 创建容器 docker run -di --name=mysql -p 33306:330 ...
- 代码解读 | VINS 视觉前端
本文作者是计算机视觉life公众号成员蔡量力,由于格式问题部分内容显示可能有问题,更好的阅读体验,请查看原文链接:代码解读 | VINS 视觉前端 vins前端概述 在搞清楚VINS前端之前,首先要搞 ...
- 游戏客户端面试(Egret)
推荐阅读: 我的CSDN 我的博客园 QQ群:704621321 一.朋友面的一家公司 1.说下let,var,const. var定义的变量,没有块的概念,可以跨块访问, 不能跨函 ...
- 在.net core web 项目中使用Nlog记录日志
第1步,添加NLog.Web.AspNetCore包引用 方法1 在项目上右击“依赖项”---“管理Nuget程序包(N)…”,然后在浏览对话框中输入“NLog.Web.AspNetCore”查找包, ...
- 利用poi包装一个简单的Excel读取器.一(适配一个Reader并提供readLine方法)
通常,读文本我们会使用BufferedReader,它装饰或者说管理了InputStreamReader,同时提供readLine()简化了我们对文本行的读取.就像从流水线上获取产品一样,每当取完一件 ...
- 第6章 面向对象的程序设计 6.1 javascript对象
ECMA-262 把对象定义为: “无序属性的集合, 其属性可以包含基本值. 对象或者函数. ” 严格来讲,这就相当于说对象是一组没有特定顺序的值.对象的每个属性或方法都有一个名字,而每个名字都映射到 ...
- Win10安装Linux系统
windows系统安装虚拟机,常见的是利用VMware Workstation这款软件来进行安装.在未接触Docker之前,我一直通过这款软件来进行管理的.docker是运行在linux环境下的,那怎 ...
- ACM-ICPC 2018 徐州赛区(网络赛)
目录 A. Hard to prepare B.BE, GE or NE F.Features Track G.Trace H.Ryuji doesn't want to study I.Charac ...