【小Y学算法】⚡️每日LeetCode打卡⚡️——40.二叉树的后序遍历

????前言

????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????

???? 每天打卡一道算法题,既是一个学习过程,又是一个分享的过程????

???? 提示:本专栏解题 编程语言一律使用 C# 和 Java 两种进行解题

???? 要保持一个每天都在学习的状态,让我们一起努力成为算法大神吧????!

???? 今天是力扣算法题持续打卡第40天????!

????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????


????原题样例:二叉树的后序遍历

给定一个二叉树,返回它的 后序 遍历。


示例 1:

输入: [1,null,2,3]  
   1
    \
     2
    /
   3 

输出: [3,2,1]

????C#方法一:迭代

仅使用一个栈完成后续遍历。步骤如下:


一直往左节点访问;

按根节点->右节点->左节点的顺序将节点依次压入栈中;

碰到叶子节点即开始出栈;

如果当前栈顶元素指向的节点依然有未访问到的子节点则回到步骤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.


????Java方法一:递归

思路和算法

首先我们需要了解什么是二叉树的后序遍历:按照访问左子树——右子树——根节点的方式遍历这棵树,而在访问左子树或者右子树的时候,我们按照同样的方式遍历,直到遍历完整棵树。

因此整个遍历过程天然具有递归的性质,我们可以直接用递归函数来模拟这一过程。


定义 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%的用户

????Java方法二:迭代

总体思路

我们也可以用迭代的方式实现方法一的递归函数,两种方式是等价的,区别在于递归的时候隐式地维护了一个栈,

而我们在迭代的时候需要显式地将这个栈模拟出来,其余的实现与细节都相同,具体可以参考下面的代码。

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%的用户

????总结

  • 今天是力扣算法题打卡的第四十天!
  • 文章采用 C# 和 Java 两种编程语言进行解题
  • 一些方法也是参考力扣大神写的,也是边学习边分享,再次感谢算法大佬们
  • 那今天的算法题分享到此结束啦,明天再见!
上一篇:根据前序遍历和中序遍历求出二叉树并打印


下一篇:Windows 10 Mobile Build 14383带来数项重要更新