[路飞]_每天刷leetcode_08(二叉树的后序遍历)

302 阅读3分钟

这是我参与11月更文挑战的第16天,活动详情查看:2021最后一次更文挑战

二叉树的后序遍历(Binary Tree Postorder Traversal)

今天我们来做一道简单的题目,二叉树的后序遍历。

LeetCode原题传送门145. 二叉树的后序遍历

题目

给定一个二叉树,返回它的 后序 遍历。 Given the root of a binary tree, return the postorder traversal of its nodes' values.

Example:

Input: root = [1,null,2,3]
Output: [3,2,1]

Input: root = []
Output: []

Input: root = [1]
Output: [1]

思考线


解题思路

首先我们要明确二叉树的遍历规则:

前序遍历:指先遍历中间节点再遍历左右子节点。

中序遍历:指先遍历左节点再遍历中间节点最后遍历右节点

后序遍历:指先遍历左右节点最后遍历中节点。

解法1

要我们进行后序遍历最简单的做法就是递归法

  1. 设置变量 res保存遍历结果的内容。
  2. 如果节点为空则直接返回
  3. 如果节点不为空进行左节点递归,右节点递归
  4. 把当前节点的值放入到res中。

代码如下


/**
 * @param {TreeNode} root
 * @return {number[]}
 */
var postorderTraversal = function(root) {
    const res = [];
    recursion(root, res);
    return res;
}

function recursion(root, res) {
    if(!root) return;
    recursion(root.left, res);
    recursion(root.right, res);
    res.push(root.val);
}

解法二

我们知道,只要是通过递归可以解决的问题,那么一定可以通过迭代来解决。我们如何实现用迭代的方式来解决这个问题呢?

我们知道递归的实现就是:每一次递归调用都会把函数的局部变量、参数值和返回地址等压入调用栈中,然后递归返回的时候,从栈顶弹出上一次递归的各项参数,所以这就是递归为什么可以返回上一层位置的原因。

那么我们用栈也可以实现二叉树的前中后序遍历。

在这之前我们先实现一下前序遍历的迭代法

/**
 * @param {TreeNode} root
 * @return {number[]}
 */
var postorderTraversal = function(root) {
    const res = [];
    const stack = [];
    if(root) stack.push(root);
    while(stack.length) {
        node = stack.pop(); // 中 出栈
        res.push(node.val);
      	// 前序遍历顺序:中-左-右,入栈顺序:中-右-左
      	if(node.right) stack.push(node.right); // 右
        if(node.left) stack.push(node.left);   // 左
    }
}

而我们又知道后序遍历顺序为 左-右-中,那么我们稍微改动一下前序遍历的顺序是不是能得到我们想要的结果的反序列呢?

我们让前序遍历的顺序(中-左-右)变为(中-右-左),那么我们再把得到的结果reverse处理就是我们想要的后序遍历结果。

说干就干,代码奉上

var postorderTraversal = function(root) {
    const res = [];
    const stack = [];
    if(root) stack.push(root);
    while(stack.length) {
        node = stack.pop(); // 中 出栈
        res.push(node.val);
      	// 前序遍历顺序:中-左-右,入栈顺序:中-右-左
        if(node.left) stack.push(node.left);   // 左
        if(node.right) stack.push(node.right); // 右
    }
  	return res.reverse();
}

以上就是我们的迭代写法。

那么能不能不通过reverse就直接得到我们的后序遍历结果呢?答案是肯定的。我们就将访问的节点放入栈中,把要处理的节点也放入栈中但是要做标记。 如何标记呢,就是要处理的节点放入栈之后,紧接着放入一个空指针作为标记。 这种方法也可以叫做标记法。

代码实现如下

var postorderTraversal = function(root) {
		const res = [];
    const stack = [];
    if(root) stack.push(root);
    while(stack.length) {
        const cur = stack.pop(); // 弹出最上层节点
        if(!cur) {
            res.push(stack.pop().val) // cur为空指针标记,则把结果放入
            continue;
        } else {
            // 后序遍历顺序:左-右-中,入栈顺序:中-右-左
            stack.push(cur);// 中
            stack.push(null);
            cur.right && stack.push(cur.right); // 右
            cur.left && stack.push(cur.left); // 左
        }
    }
    return res;
}

好了,以上就是对这道题算法的总结,利用递归实现比较简单易懂,利用迭代实现理解起来稍微复杂,需要好好理解。