问题描述
- Given a binary tree, return the postorder traversal of its nodes’ values.
- Note: Recursive solution is trivial, could you do it iteratively?
- 地址
问题分析
实现二叉树的后序遍历
- 递归
非递归
教科书法(最规范,最符合后序遍历的过程)
法2
- 类似于先序遍历得到根左右的情况,现在先得到根右左的结果(先压左,后压右),然后逆序左右根法
- 金手指法
代码实现
- 递归
public List<Integer> postorderTraversal(TreeNode root) {
List<Integer> res = new ArrayList<>();
postorderTraversal(root, res);
return res;
}
public void postorderTraversal(TreeNode root, List<Integer> res) {
if (root == null) {
return;
}
postorderTraversal(root.left, res);
postorderTraversal(root.right, res);
res.add(root.val);
}
- 非递归教科书法(最规范,最符合后序遍历的过程)
public List<Integer> postorderTraversal(TreeNode root) {
if (root == null) {
return new ArrayList<Integer>();
}
Stack<TreeNode> stack = new Stack<>();
List<Integer> res = new ArrayList<>();
stack.push(root);
//注意!!!这个初始值不具有lastPop的意思,只是为了正确开启循环罢了
TreeNode lastPrinted = root;
TreeNode peek = null;
while (! stack.isEmpty()) {
peek = stack.peek();
if (peek.left != null && lastPrinted != peek.left && lastPrinted != peek.right) {
//若上一个打印的节点既不是左孩子,也不是右孩子,那么压左孩子
stack.push(peek.left);
}else if (peek.right != null && lastPrinted != peek.right) {
//经过上一个if的过滤,若上一个打印的节点不是栈顶的右孩子,压右孩子
stack.push(peek.right);
}else {
//打印栈顶元素,出栈,并更新 lastPrinted
res.add(peek.val);
lastPrinted = stack.pop();
}
}
return res;
}
- 非递归法2
public List<Integer> postorderTraversal(TreeNode root) {
if (root == null) {
return new ArrayList<Integer>();
}
Stack<TreeNode> stack = new Stack<>();
List<Integer> res = new ArrayList<>();
stack.push(root);
//初始化
TreeNode lastPrinted = null;
while (! stack.isEmpty()) {
//当前栈顶元素
TreeNode peek = stack.peek();
if (peek.left == null && peek.right == null) {
//当前元素无左右孩子,打印出栈,并更新lastPrinted
res.add(stack.pop().val);
lastPrinted = peek;
}else if (lastPrinted != null && (lastPrinted == peek.left || lastPrinted == peek.right)) {
/*
如果上一个打印的节点是当前栈顶元素的左孩子或者右孩子,说明该节点的左孩子和右孩子都已被访问过了,(因为压栈时保证了左右根的顺序)
则同样可以直接访问该结点,lastPrinted
加lastPrinted == peek.left 的原因可能是当前节点无右孩子,同理加preNode == curNode.right
lastPrinted != null的原因是lastPrinted初始化是null,若不加,则当满足后面两个条件之一时,会打印节点,但这样是不对的
*/
res.add(stack.pop().val);
lastPrinted = peek;
}else {
/*
若存在左孩子和右孩子,先将右孩子入栈,再将左孩子入栈,保证了 每次取栈顶元素的时候,
左孩子在右孩子前面被访问,左孩子和右孩子都在根结点前面被访问。
*/
if (peek.right != null) {
stack.push(peek.right);
}
if (peek.left != null) {
stack.push(peek.left);
}
}
}
return res;
}
- 先根右左,然后逆序成左右根,注意逆序也可以不用栈
public List<Integer> postorderTraversal(TreeNode root) {
if (root == null) {
return new ArrayList<Integer>();
}
Stack<TreeNode> s1 = new Stack<>();
//s2存的是左右根的结果,用来逆序
Stack<TreeNode> s2 = new Stack<>();
s1.push(root);
while (!s1.isEmpty()) {
//出栈便“打印”,此刻打印是装入s2
TreeNode popNode = s1.pop();
s2.push(popNode);
//将出栈节点先左后右(如果非空)入栈
if (popNode.left != null) {
s1.push(popNode.left);
}
if (popNode.right != null) {
s1.push(popNode.right);
}
}
List<Integer> res = new ArrayList<>();
//装入res
while (!s2.isEmpty()) {
res.add(s2.pop().val);
}
return res;
}
- 金手指法
public List<Integer> postorderTraversal(TreeNode root) {
if (root == null) {
return new ArrayList<Integer>();
}
List<Integer> res = new ArrayList<>();
Stack<Command> stack = new Stack<>();
//initial
stack.push(new Command(false, root));
while(! stack.isEmpty()) {
Command curCommand = stack.pop();
TreeNode curRoot = curCommand.root;
if (curCommand.isPrint) {
res.add(curRoot.val);
}else {
//命令入栈顺序是根(打印)右(遍历)左(遍历)
//所以出栈顺序是左(遍历)右(遍历)根(打印),符合后序遍历的顺序
stack.push(new Command(true, curRoot));
if (curRoot.right != null) {
stack.push(new Command(false, curRoot.right));
}
if (curRoot.left != null) {
stack.push(new Command(false, curRoot.left));
}
}
}
return res;
}
}
//用于指示对root执行什么操作
class Command{
boolean isPrint;
TreeNode root;
public Command(boolean isPrint, TreeNode root) {
this.isPrint = isPrint;
this.root = root;
}
}