有没有一种方法可以从后遍历遍历中找到严格的二叉树的前遍历遍历而无需构建树?

时间:2020-03-03 14:05:08

标签: c binary-tree preorder postorder

我得到了严格的二叉树的事后遍历,并被要求找到它的事前遍历。通常,我会先构建树,然后找到预遍历。但是,我想知道是否有任何方法可以在不实际构建树的情况下找到预遍历。

3 个答案:

答案 0 :(得分:4)

[编辑:我首先假设给定的后序来自严格排序的二叉搜索树,然后回答了这个问题。 OP现在指出了我的错误并提供了示例。但这两种算法的基本原理是相同的:找到左右子树之间的边界在哪里。]

让我们考虑以下完整的二叉树的后遍历,其中每个节点都是叶子或具有两个叶子的分支:

1, 2, B, 3, 4, D, 5, C, A

我们知道数字是叶子,字母是分支。我们还知道节点A是根,因为它在后顺序遍历中排在最后。为了重建预遍历,我们必须存储根,然后依次考虑左子树和右子树。

但是,哪些节点属于左侧,哪些节点属于右侧子树?在具有 L 叶的完整叶或strictly binary tree中,有 N = 2· L − 1个节点。因此,在存储根之后,我们从右边移动其余的子数组,并跟踪节点数 N 和叶数 L 。当 N = 2· L − 1的条件成立时,我们停止。我们看到的所有内容都属于右子树,其余的都属于左子树。

所以:

int is_leaf(int c)
{
    return !isalpha(c);     // non-alphas are leaves
}

void reconst(int **pre, const int *post, int lo, int hi)
{
    printf("%d :: %d\n", lo, hi);

    if (lo < hi) {
        int k = --hi;           // will be boundary between l/r
        int root = post[k];
        int leaves = 0;
        int nodes = 0;

        while (k > lo && nodes != 2 * leaves - 1) {
            if (is_leaf(post[k - 1])) leaves++;
            nodes++;
            k--;
        }

        *(*pre)++ = root;
        reconst(pre, post, lo, k);
        reconst(pre, post, k, hi);
    }
}

这样称呼:

int post[] = {'1', '2', 'B', '3', '4', 'D', '5', 'C', 'A'};
int n = 9;
int pre[9];
int *p = pre;
int i;

reconst(&p, post, 0, n);

for (i = 0; i < n; i++) {
    printf("%c ", pre[i]);
}

puts("pre");

上面的代码依赖于几件事:(a)pre数组必须与post数组一样大,以容纳重构的预订。 (b)输入必须格式正确。该算法依赖于找到正确的完整子树。 (要防止计数器走出下限,但仅此而已。)


[原始帖子,试图从二元搜索树的后序中查找前序,而没有重复值,即严格排序。好的答案,但是因为误解了需求,所以不是OP想要的。抱歉。]

说您得到这样的后遍历:

3, 1, 7, 9, 8, 5

您知道顶部节点是(5),所有较小的节点(3,1)在左分支中,所有较大的节点(7,8,9)在右分支中。最高节点在顺序遍历中排在第一位。这样做,然后递归到表示左分支然后是右分支的子数组。

这是一个执行此操作的函数:

void reconst(int **pre, const int *post, int lo, int hi)
{
    if (lo < hi) {
        int k = --hi;                // k will be the boundary between l/r
        int parent = post[k];        // remove parent from this subarray

        // find boundary between left and right branches
        while (k > lo && post[k - 1] > parent) k--;

        *(*pre)++ = parent;          // write parent to pre-order array
        reconst(pre, post, lo, k);   // do the left subarray
        reconst(pre, post, k, hi);   // do the right subarray
    }
}

pre数组是通过指向指针的指针填充的:顶级指针跟踪pre数组中的位置,第二级指针访问基础数组。 (您可以给数组传递一个索引,如果太巴洛克的话,可以将其前进。)

这样调用函数:

int post[] = {3, 1, 7, 9, 8, 5};
int n = 6;
int pre[6];
int *p = pre;
int i;

reconst(&p, post, 0, n);

for (i = 0; i < n; i++) {
    printf("%d ", pre[i]);
}

puts("pre");

当然,用于保存预订数据的数组必须与预订数组一样大。从后提供的数据重建树的代码看起来非常相似,所以我不知道这是否真的合格。

答案 1 :(得分:0)

如果有能力重复读取原始的后遍历表示形式,则可以使用O(N²)时间但使用恒定空间(少数计数器)将后遍历转换为前遍历。依次检查源的每个字符。如果是节点,则将其丢弃。如果是叶子,请将“嵌套”计数器设置为零并检查后续字符,为每个叶子递增嵌套计数器,为每个节点递减嵌套计数器。当一个字符到达源文本末尾或该值变为负数时停止。输出在那里的节点,然后返回到找到的叶节点,以与刚刚执行的相反的方式递增和递减计数器。

计数器到达零的每个位置都将立即跟随一个节点(如果没有,则意味着计数器在到达当前位置之前已经变为负值)。输出该节点。一旦计数器到达叶子节点,计数器就开始输出并丢弃,因为它将不再需要它或之前不需要的任何东西。

这种方法不会像其他方法那样快,但是它需要最少的内存,对于某些目的可能是有利的。

答案 2 :(得分:0)

如果您假设使用任何二叉树,而不是二叉树搜索树,那么我相信不可能获得唯一的预排序序列。正如我所看到的,原因是您不知道后序序列中的第二个,第三个等节点是左子树还是右子树的根。如果该树是二叉搜索树,这很清楚,因为顺序将告诉您节点是在左侧还是右侧的子树中。

如果您只想要与提供后顺序序列的树对应的预排序序列,则可以从左侧假设不平衡的树。因此,简单地反转后序序列就是从左侧开始不平衡的树的前序序列。

例如,如果您的后继顺序为"abcd",则"dcba"是树的有效预购顺序:

      d
     /
    c
   /
  b
 /
a

也许不是您想要的,但是我认为这可以回答您的问题。

相关问题