[算法笔记] PAT-ADV-1020

题目要求:给出二叉树的后序遍历序列和中序遍历序列,输出二叉树的层次遍历序列。传送门
Sample Input

7
2 3 1 5 7 6 4
1 2 3 4 5 6 7

Sample Output

4 1 6 3 5 7 2

首先,我们在数据结构课程中学过下面的结论:

  • 后序遍历: 左 右 根
  • 中序遍历: 左 根 右

显然,后序序列中的最后一个元素总是某个子树的根,叶子节点也算是一个子树 (可能你觉得这是一句废话)。
此外, 只要确定根,那么可以在中序的某一片段序列中 “分离” 出左右子树。

下面我们来分析一下 Sample ,解析如何从如上2个结论唯一确定一颗二叉树。

在后序序列中:

2 3 1 5 7 6 4

4 毫无疑问是整个二叉树的根。现在在中序序列中找出 4 的位置。

1 2 3 4 5 6 7

也就是说可以得到二叉树的基本结构如下:

图1
        4
       / \
  1,2,3   5,6,7

再看后序序列剩下的元素:

2 3 1 5 7 6

最后一个元素是 6, 说明在 6 是根。

在中序序列中:

1 2 3 | 4 | 5 6 7

说明在 图1 中, 6 是右子树的根。

图2
         4
        / \
   1,2,3   6
          / \
         5   7

继续遍历后序序列:

2 3 1 5 7

5 和 7 既是叶子节点也是一个特殊的根。
此时,后序序列剩下:

2 3 1

中序序列为:

1 2 3

显然, 1 是根, 2 和 3 是 1 的右子树。以此类推, 2 是 3 的左子树。那么中序序列和后序序列唯一确定的二叉树如下:

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

概括一下算法要点:

  • 从右往左遍历后序序列,得到根
  • 在中序序列中找到根,分离出左右子树
  • 对左右子树进行同样的操作

不难看出,是一个递归。数据结构课程上,在纸上这类题目大家都会画出来。

最后解析一下代码实现。

  • 参数 lr: 中序序列 inorder[l...r] , 代表当前所处理的子树。
  • rootIdxPost: 后序序列中根的位置。(实际上取值变化就是: (N-1), ..., 0 )
Tree createTree(int l, int r, int &rootIdxPost)
{
    if (l > r)
        return NULL;
    if (l == r)
    {
        Tree t = new TreeNode(inorder[l]);
        rootIdxPost--;
        return t;
    }
    int rootVal = postorder[rootIdxPost--];
    int rootIdxIn = findRoot(inorder, rootVal);
    Tree root = new TreeNode(rootVal);
    root->right = createTree(rootIdxIn + 1, r, rootIdxPost);
    root->left = createTree(l, rootIdxIn - 1, rootIdxPost);
    return root;
}

完整代码:

#include <cstdio>
#include <vector>
#include <assert.h>
#include <queue>
#define NMAX 35
using namespace std;
struct TreeNode
{
    int val;
    TreeNode *left, *right;
    TreeNode(int v = -1, TreeNode *l = NULL, TreeNode *r = NULL) : val(v), left(l), right(r) {}
};
typedef TreeNode *Tree;
vector<int> postorder(NMAX), inorder(NMAX);
int N = 0;
int findRoot(vector<int> &inorder, int rootVal)
{
    size_t len = inorder.size();
    for (size_t i = 0; i < len; i++)
    {
        if (inorder[i] == rootVal)
            return i;
    }
    assert(0);
    return -1;
}
Tree createTree(int l, int r, int &rootIdxPost)
{
    if (l > r)
        return NULL;
    if (l == r)
    {
        Tree t = new TreeNode(inorder[l]);
        rootIdxPost--;
        return t;
    }
    int rootVal = postorder[rootIdxPost--];
    int rootIdxIn = findRoot(inorder, rootVal);
    Tree root = new TreeNode(rootVal);
    root->right = createTree(rootIdxIn + 1, r, rootIdxPost);
    root->left = createTree(l, rootIdxIn - 1, rootIdxPost);
    return root;
}
void level(Tree root)
{
    if (root == NULL)
        return;
    queue<Tree> q;
    printf("%d", root->val);
    if (root->left)
        q.push(root->left);
    if (root->right)
        q.push(root->right);
    while (!q.empty())
    {
        Tree p = q.front();
        q.pop();
        printf(" %d", p->val);
        if (p->left)  q.push(p->left);
        if (p->right)  q.push(p->right);
    }
}
int main()
{
    scanf("%d", &N);
    for (int i = 0; i < N; i++)
    {
        scanf("%d", &postorder[i]);
    }
    for (int i = 0; i < N; i++)
    {
        scanf("%d", &inorder[i]);
    }
    int rootIdxPost = N - 1;
    Tree root = createTree(0, N - 1, rootIdxPost);
    level(root);
}

markdown写bolg,"那是真的niubility"。

posted @ 2019-09-03 21:16  sinkinben  阅读(255)  评论(0编辑  收藏  举报