构建二叉树,是打一开始我接触 LeetCode 关于二叉树的题时就在想的问题了。

这个问题不太好想,但它的逆问题却非常成熟了,即如何遍历一颗二叉树?

稍微总结一下:

  • 遍历二叉树
    • 深度优先遍历 –> (DFS) –> (stack)
      • 先序遍历 –> (先根遍历) –> (DFS) –> (stack, 寻左右压栈)
      • 中序遍历 –> (中根遍历) –> (DFS) –> (stack, 寻左根压栈, 取根再取右)
      • 后续遍历 –> (后根遍历) –> (DFS) –> (stack, 寻左根压栈, 取右再取根)
    • 广度优先遍历 –> (BFS) –> (queue)

首先是整体分类,按深度还是按广度?接着就是核心的算法思想:DFS 还是 BFS ?最后就是核心的数据结构,stack 还是 queue ?

我们从 tree 这种数据结构出发,经历了各种算法思想,最后又回到了本质的 stack or queue 这种数据结构上来。实际上是一种分治简化的策略,树作为一种中级数据结构来说,还是比较抽象的。

我们深度挖掘它,就会回到低一级的数据结构上去,如果再深度挖掘 stack or queue 呢?就会回到 LinkedList or array 上去。

所以数据结构既是算法的基础,却也是算法的抽象。我们学习数据结构时,不要将算法割裂开,它们是紧密联系相关的。


上面这段内容,纯粹是对 tree 基础知识的一个回顾与总结。我们说起 tree 总会先谈起如何遍历它。而这道题妙哉,反其道而行,让你组建它。

我们再次看看上面的分类图,可以很明显的发现,中序与后续,惊人的相似,连压栈规则都一致,仅仅是出栈规则有略微不同而已。这也是为什么 LeetCode 专门考察了 08. Binary Tree Inorder Traversal09. Binary Tree Preorder Traversal,并没有再去专门考察 Postorder.

而正因为他们算法思想的相似,所以逆向生成二叉树的时候,最容易的方式,也是从它俩开始着手。


下面实例说明:

        1
      /  \
     2    3
    / \   /
   4   5 6
  /   /   \
 7   8     9

// inorder: 7,4,2,8,5,1,6,9,3
//              ^     ^     ^
// postorder: 7,4,8,5,2,9,6,3,1
//                    ^     ^ ^

可以看到的规律:

  1. postorder.back() == 根节点的值
  2. inorder中,根节点将左右子树分隔
  3. 通过 inorder 中左右子树的 size ,可以在 postorder 中轻松找到 左右子树的根节点。

通过上述三条规律,可以很容易的总结出递归特性。

最终解法限于递归,思路比较清晰,但效率较低,可以改为迭代一试。

#include <cstddef>
#include <vector>
#include <algorithm>

using namespace std;

struct TreeNode {
    int val;
    TreeNode *left;
    TreeNode *right;
    TreeNode(int x) : val(x), left(NULL), right(NULL) {}
};
 
class Solution {
public:
    using vcIt = vector<int>::const_iterator;
    TreeNode *buildTree(vector<int> &inorder, vector<int> &postorder) {
        return buildTree(inorder.cbegin(), inorder.cend(), postorder.cbegin(), postorder.cend());
    }
    TreeNode *buildTree(vcIt inBeg, vcIt inEnd, vcIt postBeg, vcIt postEnd) {
        if (inBeg >= inEnd || postBeg >= postEnd) return NULL;
        TreeNode *root = new TreeNode(*prev(postEnd));
        auto inRoot = find(inBeg, inEnd, root->val);
        root->left = buildTree(inBeg, inRoot, postBeg, next(postBeg, inRoot-inBeg));
        root->right = buildTree(next(inRoot), inEnd, next(postBeg, inRoot-inBeg), prev(postEnd));
        return root;
    }
};

LeetCode Direct Link