给定一个二叉树,返回它的 后序 遍历。
示例 1:
输入: [1,null,2,3]
1
\
2
/
3
输出: [3,2,1]
仅使用一个栈完成后续遍历。步骤如下:
难点在于如何判断栈顶节点是否有未访问的子节点。 如果判断方式不当,很可能会因为栈顶节点是上一个已出栈节点的父节点,而导致其节点反复入栈出栈陷入死循环。
可以增加一个变量或指针,用于指向前一个已经出栈的节点。
每次判断栈顶节点的时候只需要提前判断一次是否与上一个出栈节点是父子关系,是的话则继续出栈,否则回到上面的步骤1再进行入栈循环。
public class Solution {
public IList<int> PostorderTraversal(TreeNode root) {
List<int> rst = new List<int>();
if(root == null) return rst;
Stack<TreeNode> st = new Stack<TreeNode>();
TreeNode node = root;
TreeNode printNode = root;
TreeNode tmp = null;
st.Push(node);
while(st.Any())
{
tmp = null;
if(node.left != printNode && node.right != printNode)
{
if(node.right != null)
{
st.Push(node.right);
tmp = node.right;
}
if(node.left != null)
{
st.Push(node.left);
tmp = node.left;
}
}
node = tmp;
if(tmp == null)
{
rst.Add((printNode = st.Pop()).val);
if(st.Any())
node = st.Peek();
}
}
return rst;
}
}
执行结果
执行通过,执行用时284ms,内存消耗30.1MB.
思路和算法 首先我们需要了解什么是二叉树的后序遍历:按照访问左子树——右子树——根节点的方式遍历这棵树,而在访问左子树或者右子树的时候,我们按照同样的方式遍历,直到遍历完整棵树。 因此整个遍历过程天然具有递归的性质,我们可以直接用递归函数来模拟这一过程。
定义 postorder(root) 表示当前遍历到 root 节点的答案。 按照定义,我们只要递归调用 postorder(root->left) 来遍历 root 节点的左子树,然后递归调用 postorder(root->right) 来遍历 root 节点的右子树,最后将 root 节点的值加入答案即可,递归终止的条件为碰到空节点。
class Solution {
public List<Integer> postorderTraversal(TreeNode root) {
List<Integer> res = new ArrayList<Integer>();
postorder(root, res);
return res;
}
public void postorder(TreeNode root, List<Integer> res) {
if (root == null) {
return;
}
postorder(root.left, res);
postorder(root.right, res);
res.add(root.val);
}
}
执行结果 执行通过
执行用时 0 ms, 在所有 Java 提交中击败了100.00%的用户
内存消耗 36.6 MB, 在所有 Java 提交中击败了68.49%的用户
总体思路 我们也可以用迭代的方式实现方法一的递归函数,两种方式是等价的,区别在于递归的时候隐式地维护了一个栈,
而我们在迭代的时候需要显式地将这个栈模拟出来,其余的实现与细节都相同,具体可以参考下面的代码。
class Solution {
public List<Integer> postorderTraversal(TreeNode root) {
List<Integer> res = new ArrayList<Integer>();
if (root == null) {
return res;
}
Deque<TreeNode> stack = new LinkedList<TreeNode>();
TreeNode prev = null;
while (root != null || !stack.isEmpty()) {
while (root != null) {
stack.push(root);
root = root.left;
}
root = stack.pop();
if (root.right == null || root.right == prev) {
res.add(root.val);
prev = root;
root = null;
} else {
stack.push(root);
root = root.right;
}
}
return res;
}
}
执行结果
执行结果 执行通过
执行用时 0 ms, 在所有 Java 提交中击败了100.00%的用户
内存消耗 36.7 MB, 在所有 Java 提交中击败了40.52%的用户