1、题目描述
给你一棵二叉树的根节点 root
,返回其节点值的 后序遍历 。
示例 1:
输入:root = [1,null,2,3]
输出:[3,2,1]
解释:
示例 2:
输入:root = [1,2,3,4,5,null,8,null,null,6,7,9]
输出:[4,6,7,5,2,9,8,3,1]
解释:
示例 3:
输入:root = []
输出:[]
示例 4:
输入:root = [1]
输出:[1]
提示:
- 树中节点的数目在范围
[0, 100]
内 -100 <= Node.val <= 100
进阶:递归算法很简单,你可以通过迭代算法完成吗?
2、方法1:迭代
解题思路
迭代法通过显式使用栈模拟递归过程,避免递归栈溢出问题。后序遍历的迭代实现需要额外记录已访问的右子树。
步骤:
-
初始化:创建空栈和空列表,从根节点开始遍历。
-
遍历左子树:
-
将当前节点及其左子节点依次压入栈中,直到左子节点为空。
-
-
处理右子树:
-
弹出栈顶节点,检查其右子树是否已访问或为空。
-
若右子树未访问,则将当前节点重新压栈,并转向右子节点。
-
若右子树已访问或为空,则访问当前节点,并记录为已访问节点。
-
-
终止条件:栈为空且当前节点为空时结束。
时间复杂度:O(n),空间复杂度:O(n)(栈空间)
public List<Integer> postorderTraversal(TreeNode root) {
List<Integer> list = new ArrayList<>();
Stack<TreeNode> stack = new Stack<>();
TreeNode curNode = root;
TreeNode preNode = null; // 记录前一个访问的节点
while (curNode != null || !stack.isEmpty()) {
// 遍历左子树
while (curNode != null) {
stack.push(curNode);
curNode = curNode.left;
}
curNode = stack.pop();
// 判断右子树是否已访问或为空
if (curNode.right == null || curNode.right == preNode) {
list.add(curNode.val); // 访问当前节点
preNode = curNode; // 记录已访问节点
curNode = null; // 避免重复处理左子树
} else {
stack.push(curNode); // 重新压栈以处理右子树
curNode = curNode.right;
}
}
return list;
}
3、方法2:递归
解题思路
递归法直接利用函数的调用栈实现后序遍历,代码简洁但可能因递归深度过大导致栈溢出。
步骤:
-
递归终止条件:当前节点为空时,直接返回。
-
递归左子树:对当前节点的左子节点调用递归函数。
-
递归右子树:对当前节点的右子节点调用递归函数。
-
访问节点:将当前节点的值加入结果列表。
时间复杂度:O(n),空间复杂度:O(n)(调用栈)
public List<Integer> postorderTraversal(TreeNode root) {
List<Integer> list = new ArrayList<>();
flowTree(root, list);
return list;
}
public void flowTree(TreeNode node, List list) {
if (node == null) return;
flowTree(node.left, list); // 递归左子树
flowTree(node.right, list); // 递归右子树
list.add(node.val); // 访问当前节点
}