目录
题目要求:给你一棵二叉树的根节点 root ,返回其节点值的 后序遍历
方法一:递归
方法二:迭代
思路分析:
代码展示:
复杂度分析
方法三:迭代进阶
思路分析:
代码展示:
题目要求:给你一棵二叉树的根节点 root
,返回其节点值的 后序遍历
示例 1:
输入:root = [1,null,2,3] 输出:[3,2,1]
方法一:递归
递归的方法和二叉树的前序以及中序遍历在之前的博客中已经写过,需要的小伙伴可以点击链接查看
递归求二叉树的前中后序遍历
【LeetCode】二叉树的前序遍历(递归,迭代,Morris遍历)
【LeetCode】二叉树的中序遍历(递归,迭代,Morris遍历)
这篇文章主要来讲解非递归的方法对二叉树进行后序遍历
方法二:迭代
思路分析:
迭代的方式其实与递归是等价的,区别在于递归的时候隐式地维护了一个栈,而我们在迭代的时候
需要显式地将这个栈模拟出来,其余的实现与细节都相同,具体可以参考下面的代码
其实后序遍历的思想与中序遍历大差不差,只不过中序是左根右,在栈中弹出的元素其左子树都是已访问完的,此时我们可以直接访问该节点,再继续访问它的右子树就是了
但后续遍历是左右根,此时我们只能保证该节点的左子树访问完成,但却不知晓右子树的情况,此时我们只需维护一个节点,用来帮助我们判断该节点的右子树有没有被访问过即可
代码展示:
class Solution {
public List<Integer> postorderTraversal(TreeNode root) {
//栈和线性表
List<Integer> list = new ArrayList<>();
if(root == null){
return list;
}
Deque<TreeNode> stack = new LinkedList<>();
TreeNode prev = null;
while(root != null || !stack.isEmpty()){
//root非空
while(root != null){
stack.push(root);
root = root.left;
}
//此时root为空,我们需要判断右子树是否走过或者为空
root = stack.pop();
if(root.right == null || root.right == prev){
list.add(root.val);
prev = root;
root = null;
}else{
stack.push(root);
root = root.right;
}
}
return list;
}
}
复杂度分析
-
时间复杂度:O(n),其中 n 是二叉树的节点数。每一个节点恰好被遍历一次。
-
空间复杂度:O(n),为迭代过程中显式栈的开销,平均情况下为 O(logn),最坏情况下树呈现链状,为 O(n)
方法三:迭代进阶
思路分析:
我们都知道前序遍历与后序遍历的差别就是根的位置的关系
前序遍历顺序为:根 -> 左 -> 右
后序遍历顺序为:左 -> 右 -> 根
如果我们用链表来存储节点,并且改为头插的方式,那么按照前序遍历的结果
链表就变为了:右 -> 左 -> 根
我们将遍历的顺序由从左到右修改为从右到左
结果链表就变为了:左 -> 右 -> 根,这刚好就是后序遍历的结果
代码展示:
class Solution {
public List<Integer> postorderTraversal(TreeNode root) {
List<Integer> list = new ArrayList<>();
if(root == null){
return list;
}
Deque<TreeNode> stack = new LinkedList<>();
stack.push(root);
while(!stack.isEmpty()){
TreeNode x = stack.pop();
list.add(0,x.val);
if(x.right != null){
stack.push(x.right);
}
if(x.left != null){
stack.push(x.left);
}
}
return list;
}
}