数据结构之二叉树篇卷三 -- 二叉树非递归遍历(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 ...
随机推荐
- java多线程理解及实现
其实多线程也很好理解,就好比我们坐高铁.飞机过安检一样,过安检的时候一个入口只有一个安检口,而多线程就是为安检开启了多个安检口,话不多说贴代码 线程实现的三种方式: 一.继承Thread类创建线程类 ...
- NMS的python实现
https://blog.csdn.net/a1103688841/article/details/89711120
- 欧几里得(Euclid)与拓展的欧几里得算法
欧几里得(Euclid)与拓展的欧几里得算法 欧几里得(Euclid)与拓展的欧几里得算法 欧几里得算法 原理 实现 拓展的欧几里得算法 原理 递归求解 迭代求解 欧几里得算法 原理 欧几里得算法是一 ...
- 杭电多校第十场 hdu6435 CSGO 二进制枚举子集
CSGO Time Limit: 4000/2000 MS (Java/Others) Memory Limit: 524288/524288 K (Java/Others)Total Subm ...
- 牛客网暑期ACM多校训练营(第四场) G Maximum Mode 思维
链接:https://www.nowcoder.com/acm/contest/142/G来源:牛客网 The mode of an integer sequence is the value tha ...
- C++数据类型(data type)介绍
在编写程序时,数据类型(data type)定义了使用存储空间的(内存)的方式. 程序员通过定义数据类型(data type),告诉特定存储空间这里要存储的数据类型是什么,以及你即将操作他的方式.(注 ...
- DataReader转换
public static partial class Extension { private static ConcurrentDictionary<Type, ConcurrentDicti ...
- POJ 2391 Ombrophobic Bovines(Floyd+二分+最大流)
题目链接 题意:农场有F(1 <= F <= 200)片草地用于放牛,这些草地有P(1 <= P <= 1500)连接,农场的草地上有一些避雨点,奶牛们可以在避雨点避雨,但是避 ...
- 【Offer】[3-1] 【找出数组中重复的数字】
题目描述 思路 Java代码 代码链接 题目描述 在一个长度为n的数组里的所有数字都在0~n-1的范围内.数组中某些数字是重复的,但不知道有几个数字重复了,也不知道每个数字重复了几次. 请找出数组中任 ...
- 【LeetCode】46-全排列
题目描述 给定一个没有重复数字的序列,返回其所有可能的全排列. 示例: 输入: [1,2,3] 输出: [ [1,2,3], [1,3,2], [2,1,3], [2,3,1], [3,1,2], [ ...