用递归和非递归方式实现二叉树的先序中序后序遍历
Posted wangkaipeng
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了用递归和非递归方式实现二叉树的先序中序后序遍历相关的知识,希望对你有一定的参考价值。
很久没写博客了,也很久没有静下心来学习技术,具体原因不再多纠结。
最近完成零丁任务之余每天刷一刷LeetCode,看看书(比如这篇记录的是左程云大佬的《程序员代码面试指南》中的内容)
温习和学习一些算法以及相关知识,巩固一下基础。
算法的程序代码大多不是自己初次AC所写,因为在翻阅书籍和欣赏discuss区大佬的代码与100%击败率的代码时,
经常能发现更为简洁和漂亮的写法,有时候甚至能发现某些“惊为天人”的操作(基操,勿6)。
内容可能和以前写的有所重复,大同小异,换了java的实现,然后有所拓展。
好吧,渣渣的逼逼叨叨到此结束。
树结构定义
public class TreeNode {
public int val;
public TreeNode left;
public TreeNode right;
public TreeNode(int x) {
this.val = x;
}
}
先序、中序、后序指的是根节点,先序:根左右;中序:左根右;后序:左右根。
递归方式如下,之间的差别就是调整了输出语句的位置。
public void preOrderRecur(TreeNode root) {
if (root == null)
return;
System.out.println(root.val);
preOrderRecur(root.left);
preOrderRecur(root.right);
}
public void inOrderRecur(TreeNode root) {
if (root == null)
return;
inOrderRecur(root.left);
System.out.println(root.val);
inOrderRecur(root.right);
}
public void posOrderRecur(TreeNode root) {
if (root == null)
return;
posOrderRecur(root.left);
posOrderRecur(root.right);
System.out.println(root.val);
}
用递归方法解决的问题都能用非递归的方法实现。因为递归方法利用了函数栈来保存信息,如果用自己申请的数据结构代替函数栈,也能实现相同功能。
非递归的先序遍历:
1、申请栈,头节点入栈;
2、弹出栈顶并打印,右孩子入栈(不null),左孩子入栈(不null);
3、不断重复步骤2,直到栈空。
public void preOrderUnRecur(TreeNode root) {
if (root == null)
return;
Stack<TreeNode> s = new Stack<TreeNode>();
s.push(root);
while (!s.isEmpty()) {
root = s.pop();
System.out.println(root.val);
if (root.right != null)
s.push(root.right);
if (root.left != null)
s.push(root.left);
}
}
非递归的中序遍历:
1、申请栈,令cur=root;
2、cur入栈,不断令cur=cur.left并入栈;
3、cur==null时,从栈中弹出一个节点node并打印,令cur=node.right,重复步骤2;
4、重复2、3直到栈空且cur空。
public void inOrderUnRecur(TreeNode root) {
if (root == null)
return;
Stack<TreeNode> s = new Stack<TreeNode>();
while (!s.isEmpty() || root != null) {
if (root != null) {
s.push(root);
root = root.left;
} else {
root = s.pop();
System.out.println(root.val);
root = root.right;
}
}
}
非递归的后序遍历:
首先是两个栈的实现方式,比较好理解:
1、申请栈s1,s2,头节点 入s1;
2、s1中弹出节点记为cur,cur压入s2,cur左右孩子依次压入s1;
3、重复2直到s1为空;
4、从s2中不断出栈并打印。
解释:每颗子树的头节点都最先从s1中弹出,然后把该节点的孩子按照先左再右的顺序压入s1,
那么从s1中弹出的顺序就是先右再左,总的顺序就是中右左,则从s2中弹出的顺序是左右中(逆s1)。
// two stacks
public void posOrderUnRecur1(TreeNode root) {
if (root == null)
return;
Stack<TreeNode> s1 = new Stack<TreeNode>();
Stack<TreeNode> s2 = new Stack<TreeNode>();
s1.push(root);
while (!s1.isEmpty()) {
// 每颗子树的头节点都最先从s1中弹出
root = s1.pop();
s2.push(root);
// 先左后右压入,总的出栈顺序为中右左,则s2出栈顺序为左右中
if (root.left != null)
s1.push(root.left);
if (root.right != null)
s1.push(root.right);
}
while (!s2.isEmpty())
System.out.println(s2.pop().val);
}
然后是一个栈的实现方式:
1、申请栈,头节点入栈,设置变量h和c,h代表最近一次弹出并打印的节点,c代表栈顶节点,初试时h为头节点,c为null;
2、令c=stack.peek(); //即令c等于栈顶节点,但栈顶不弹出,分三种情况:
Ⅰ 如果 c.left != null,且 h != c.left,且 h!=c.right,则c的左孩子入栈。
解释:h代表最近一次弹出并打印的节点,如果 h==c.left 或者 h==c.right,说明c的左子树和右子树已经打印完毕,
不应该再把c的左孩子入栈。否则,说明c的左子树还没有处理过,则c的左孩子入栈。
Ⅱ 如果条件Ⅰ不成立,且 c.right != null,且 h != c.right,则c的右孩子入栈。
Ⅲ 如果Ⅰ和Ⅱ都不成立,说明c的左子树和右子树都打印完毕,则从栈中弹出c并打印,令h=c。
3、重复步骤2直到栈为空。
// h代表最近一次弹出并打印的节点,c代表stack的栈顶节点
// 初试时h为头节点,c为null
public void posOrderUnRecur2(TreeNode h) {
if (h == null)
return;
Stack<TreeNode> s = new Stack<TreeNode>();
s.push(h);
TreeNode c = null;
while (!s.isEmpty()) {
c = s.peek();
if (c.left != null && h != c.left && h != c.right)
s.push(c.left);
else if (c.right != null && h != c.right)
s.push(c.right);
else {
System.out.println(s.pop().val);
h = c;
}
}
}
以上是关于用递归和非递归方式实现二叉树的先序中序后序遍历的主要内容,如果未能解决你的问题,请参考以下文章