二叉树重建[UVA-536]

二叉树重建Tree Recovery(UVA-536,ULM1997)

 时间限制3000ms

分别是书上习题6-3,涉及到二叉树这一数据结构中已知两种遍历求整个树的过程。

首先需要复习一下二叉树的前序遍历和中序遍历和后序遍历。二叉树的前序遍历是指,先输出根结点的值,然后依次递归调用遍历左右子树;中序遍历是指,先递归遍历左子树,然后输出根结点的值,然后递归遍历右子树;后续遍历是指,先递归遍历左右子树,然后输出根结点的值。下面的解题分析都是以此为基础的。

以第一个样例输入为例:

先序遍历:DBACEGF

中序遍历:ABCDEFG

前面提到,前序遍历首先会输出当前子树的根结点,因此位于前序遍历的第一个元素D,一定是当前树的根。而在中序遍历中,根在中间输出,因而在中序遍历中查找根D,将整个中序遍历分为了两大部分:分别是树的左子树和右子树,如下所示

D

/   \

(ABC) (EFG)

那左右子树又该如何得到呢?很容易想到用递归的方法来构建,也就是利用了自顶向下的求解思路。左右子树的中序遍历很容易得到(利用找到的根的位置将其一分为二),而如何得到在原前序遍历中的子树呢?我们知道,前序遍历在输出根结点后,依次输出其左右的结点,所以关键的一步在于确定左右子树的结点个数。而利用之前中序遍历找到的根的位置,不难确定其个数。因此整个重建的算法就很明了了。剩下的问题就是如何确定递归出口。也很简单,只要输入时的数组只有一个元素,即对应的树没有孩子,那么该结点实际就是原树的一个叶子,递归也就结束了。整个过程的时间为$O(n^2)$。

我的的C++代码如下:(我的二叉树是按照《算法导论》一书中的描述定义的,包含了父节点。所以需要额外的处理一下。)

 1 #include<iostream>
 2 #include<algorithm>
 3 #include<cstring>
 4 template<typename T>
 5 struct treenode
 6 {
 7     T value;
 8     treenode * leftchild;
 9     treenode * rightchild;
10     treenode * parent;
11     treenode()
12     {
13         value = 0; leftchild = rightchild = parent = nullptr;
14     }
15     treenode(int v) :value(v)
16     {
17         leftchild = rightchild = parent = nullptr;
18     }
19 };
20 template<typename T>
21 int search(T A[], T value, int size)
22 {
23     int i;
24     for (i = 0; i < size; i++)
25         if (A[i] == value)
26             return i;
27     return -1;
28 }
29 template<typename T>
30 treenode<T> * treerecovery(T inorder[], T postorder[], int size)
31 {
32     if (size == 1)
33         return new treenode<T>(inorder[0]);
34     int pos = search(inorder, postorder[0], size);
35     treenode<T> * ptleft = nullptr;
36     treenode<T> * ptright = nullptr;
37     treenode<T> * ptthis = new treenode<T>(postorder[0]);
38     if (pos != 0)
39     {
40
41         ptleft = treerecovery(inorder, postorder + 1, pos);
42         ptleft->parent = ptthis;
43     }
44     if (pos != size - 1)
45     {
46         ptright = treerecovery(inorder + pos + 1, postorder + pos + 1, size - pos - 1);
47         ptright->parent = ptthis;
48     }
49     ptthis->leftchild = ptleft;
50     ptthis->rightchild = ptright;
51     return ptthis;
52 }
53 template<typename T>
54 void traversep(treenode<T> * tn)
55 {
56     if (tn == nullptr)
57         return;
58     treenode<T> * pt = tn;
59     traversep(tn->leftchild);
60     traversep(tn->rightchild);
61     std::cout << tn->value;
62 }
63 int main()
64 {
65 #define TMAX 30
66     char A[TMAX];
67     char B[TMAX];
68     while (std::cin >> A)
69     {
70         std::cin >> B;
71         int len = strlen(A);
72         treenode<char> * tree = treerecovery(B, A, len);
73         traversep(tree);
74         std::cout << std::endl;
75     }
76     return 0;
77 }

需要说明的是,必须要有中序遍历,才能够确定唯一的二叉树。换言之,只有前序和后序遍历是无法得到唯一确定的二叉树的。为什么呢?理由很简单,没有办法确定这个树左右节点是不是满的。以输入用例为例,只知道根节点D,剩余的元素,完全可以作为单纯的左子树或者右子树,也可以作为两支。

时间: 2024-10-13 10:54:22

二叉树重建[UVA-536]的相关文章

UVA 548(二叉树重建与遍历)

J - Tree Time Limit:3000MS     Memory Limit:0KB     64bit IO Format:%lld & %llu Submit Status Appoint description:  System Crawler  (2014-05-16) Description  Tree  You are to determine the value of the leaf node in a given binary tree that is the ter

二叉树重建(衔接上一篇二叉树基本讲解)

[题目]输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树,假设输入的前序遍历和中序遍历的结果中都不含有重复的数字,例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},重建二叉树并输出头结点. [分析]对根节点和左子树右子树分别分析 [根节点]前序遍历结果和中序遍历结果可以唯一确定一棵二叉树,前序遍历的过程就是从根结点开始,先访问根结点,再遍历左子树,最后右子树,从{1,2,4,7,3,5,6,8}可以看出1为根节点,中序遍历的过程是先遍

UVa 二叉树重建(先序+中序求后序)

题意是给出先序和中序,求出后序. 先序遍历先访问根结点,通过根结点可以在中序中把序列分为左子树部分和右子树部分,我建了一个栈,因为后序遍历最后访问根结点,所以把每次访问的根结点放入栈中.因为后序遍历先是左子树然后是右子树,所以在递归的时候就先递归右子树,然后继续递归左子树. 写完程序后有个错误,找了很久才发现,就是我原本在计算左子树个数的时候,是这样计算的,pre2=mid-pre,但是当pre>mid时,就不对了.而正确计算左子树的方法应该是下面这样的. 1 #include<iostrea

二叉树重建

输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树.假设输入的前序遍历和中序遍历的结果中都不含重复的数字.例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建二叉树并输出它的后序遍历序列. 代码: package com.huawei; import java.util.Scanner; class Node { Node left = null; Node right = null; int value; int size;}pu

二叉树重建及二叉树广度优先遍历

#include <iostream> #include <queue> using namespace std; //树节点类 class TNode { public: TNode *left, *right; char value; TNode() { left = right = NULL; } TNode(char v) { left = right = NULL; value = v; } }; //根据二叉树的先序遍历和中序遍历重建二叉树 //PreOrder为二叉树

UVa 536 Tree Recovery | GOJ 1077 Post-order (习题 6-3)

传送门1: https://uva.onlinejudge.org/external/5/536.pdf 传送门2: http://acm.gdufe.edu.cn/Problem/read/id/1077 题意一样   输入不一样 HINT: 1. Preorder : (root, left subtree, right subtree); Inorder : (left subtree, root, right subtree); Postorder: (left subtree, rig

uva 536

给出二叉树先序和中序遍历打印出后序遍历结果. 这一题跟之前的 uva 548有一些类似,可以对照着学习 代码如下,特备注意dfs的时候递归条件心里要清楚,否则出错很麻烦: #include <iostream> #include <cstdio> #include <cstdlib> #include <cstring> #include <map> #include <set> using namespace std; const

UVa 536 Tree Recovery(先序,中序求后序)

题意  给你二叉树的先序序列和中序序列  求它的后序序列 先序序列的第一个一定是根  中序序列根左边的都属于根的左子树  右边的都属于右子树  递归建树就行了 #include <bits/stdc++.h> using namespace std; typedef struct TNode { char data; TNode *lc, *rc; } node, *BTree; void build(BTree &t, string pre, string in) { t = new

二叉树重建 - (先序遍历、中序遍历、后序遍历)

对于一棵二叉树T,我们可以递归定义它的先序遍历,中序遍历,后序遍历:  1.先序遍历  ( PreOrder(T) = T的根节点 + PreOrder(T的左子树) + PreOrder(T的右子树) ) 2.中序遍历  ( InOrder(T) = InOrder(T的左子树) + T的根节点 +  InOrder(T的右子树) ) 3.后序遍历  ( PostOrder(T) = PostOrder(T的左子树) + PostOrder(T的右子树)  + T的根节点 ) 其中,加号表