三序遍历的遍历顺序
先序:根->左->右(非常简单,一个深度搜索搞定,逻辑清晰)
中序:左->根->右(算法比较复杂)
后序:左->右->根(邪道很简单,基于反向先序的逆序,借助双栈,正道借助单栈)
后序遍历的邪道算法
算法的基本灵感:
如图所示:
先序遍历是:ABDEGCFHI
后序遍历是:DGEBHIFCA
我们往后的实验场景通常会基于这个场景,所以我们一般需要先构造出这样一个树:
package BinaryTree;
public class BinaryTreeCase {
public BinaryTreeNode tree_1(){
BinaryTreeNode A = new BinaryTreeNode("A");
BinaryTreeNode B = new BinaryTreeNode("B");
BinaryTreeNode C = new BinaryTreeNode("C");
BinaryTreeNode D = new BinaryTreeNode("D");
BinaryTreeNode E = new BinaryTreeNode("E");
BinaryTreeNode F = new BinaryTreeNode("F");
BinaryTreeNode G = new BinaryTreeNode("G");
BinaryTreeNode H = new BinaryTreeNode("H");
BinaryTreeNode I = new BinaryTreeNode("I");
A.left = B;
A.right = C;
B.left = D;
B.right = E;
E.left = G;
C.right = F;
F.left = H;
F.right = I;
return A;
}
}
public class BinaryTreeNode {
public BinaryTreeNode left;
public BinaryTreeNode right;
public int value;
/**
* 字符变量
*/
public String sVal;
public BinaryTreeNode(String vl){
sVal = vl;
}
}
先来做一个简单的先序遍历preOrder();
public static List<String> preOrder(BinaryTreeNode head){
List<String> list = new ArrayList<String>();
Stack<BinaryTreeNode> stack = new Stack<BinaryTreeNode>();
stack.push(head);
while (!stack.isEmpty()){
BinaryTreeNode cur = stack.pop();
list.add(cur.sVal);
//出栈顺序:左->右
//入栈顺序:右->左
if(cur.right!= null){
stack.push(cur.right);
}
if(cur.left!= null){
stack.push(cur.left);
}
}
return list;
}
结果是:
然后,我们将入栈顺序变成了左->又
public static List<String> evilWaysOrder(BinaryTreeNode head){
List<String> list = new ArrayList<String>();
Stack<BinaryTreeNode> stack = new Stack<BinaryTreeNode>();
stack.push(head);
while (!stack.isEmpty()){
BinaryTreeNode cur = stack.pop();
list.add(cur.sVal);
//出栈顺序:左->右
//入栈顺序:右->左
//邪道入栈:左->右
if(cur.left!= null){
stack.push(cur.left);
}
if(cur.right!= null){
stack.push(cur.right);
}
}
return list;
}
结果变成:
然后和正确的后序遍历结果
你会发现,只需要进行简单的翻转就成
...所以这种方法非常邪道...
最终代码:
public static List<String> evilWaysOrder(BinaryTreeNode head){
List<String> list = new ArrayList<String>();
Stack<BinaryTreeNode> stack = new Stack<BinaryTreeNode>();
Stack<String> reverseStack = new Stack<String>();
stack.push(head);
while (!stack.isEmpty()){
BinaryTreeNode cur = stack.pop();
reverseStack.push(cur.sVal);
//出栈顺序:左->右
//入栈顺序:右->左
//邪道入栈:左->右
if(cur.left!= null){
stack.push(cur.left);
}
if(cur.right!= null){
stack.push(cur.right);
}
}
while (!reverseStack.isEmpty()){
list.add(reverseStack.pop());
}
return list;
}
这种方法,只能在面试的时候,突然忘了正道的后序遍历方法时,用的,从学习算法的角度来讲,不是非常推荐使用这种方法来进行操作
正道后序遍历
所谓道高一尺魔高一仗,正道的修行往往会比魔道更加复杂,哎
看着解法就很恶心,但是,就算你用一种偏门方法解决了这种“特情”,那么遇到更多的情况,你又凭借什么办法去适应呢?所以
正道功法虽然修行缓慢,但是却中正平和。
回顾后序遍历的顺序,左->右->根
DGEBHIFCA
不管是先序,还是后序,使用栈是最基本的,现在主要讨论的是入栈的顺序
这个时候,还是希望直觉能够起到一定作用,毕竟算法题除非灵光一闪,你突然悟了,你就真的懂了,不然,这个过程只能靠直觉去寻找防线
首先第一点,仅从答案DGEBHIFCA,我们是无法强行逆推入栈顺序的因为可能D如D出,E,G入,G,E出...不能直接想象成ACFIH...的入栈顺序去强行怼上去
得从原理来:后序遍历的顺序是
左->右->根
所以如果理解不了,强背吧,我也理解不了
压完左边压右边
右边也是一棵树
所以还得压左边
没点压了能出栈
栈顶变则右树出
新轮回栈压左边
无穷无尽是bug
添加记号禁重压
无穷右树具显化
好诗,好诗~
public static ArrayList<String> postOrder3(TreeNode root) {
TreeNode pointer = root;
TreeNode banSign = null;
Stack<TreeNode> stack = new Stack<TreeNode>();
ArrayList<String> list = new ArrayList<String>();
while (!stack.isEmpty()||pointer!= null){
if(pointer!= null){
//压完左边
stack.push(pointer);
pointer = pointer.left;
}
if(pointer == null){
pointer = stack.peek().right;
//禁重压
if(pointer!= null&&pointer!= banSign){
//压右边
stack.push(pointer);
//右边也是一棵树
//所以还得压左边
pointer = pointer.left;
}else {
//没点压了能出栈
TreeNode temp = stack.pop();
list.add(temp.sVal);
//栈顶变则右树出
pointer = null;
//添加标记
banSign = temp;
}
}
}
return list;
}