二叉树先序,中序,后序遍历之间的相互求法

首先我们要清楚的是,二叉树遍历的三种形式:

1.先(根)序遍历的递归算法定义:

若二叉树非空,则依次执行如下操作:

⑴ 访问根结点;

⑵ 遍历左子树;

⑶ 遍历右子树。


2.中(根)序遍历的递归算法定义:


若二叉树非空,则依次执行如下操作:


⑴遍历左子树;

⑵访问根结点;

⑶遍历右子树。


3.后(根)序遍历得递归算法定义:

若二叉树非空,则依次执行如下操作:


⑴遍历左子树;

⑵遍历右子树;

⑶访问根结点。

既然根据一棵已经存储好数据而建立起的二叉树,能够有三种方式对其中存储的数据进行访问。那么我们是否能够根据这三种方式访问的结果来建立起一棵二叉树或者说是根据其中的两种遍历方式的结果,求解出另外一种遍历的结果呢?

答案是肯定的,在这里,我们主要研究根据其中的两种遍历方式的结果,求解出另外一种遍历的结果

有这样的一道试题:

题目链接:POJ2255

题目描述:

Tree Recovery
Time Limit: 1000MS Memory Limit: 65536K
Total Submissions: 13325 Accepted: 8312

Description

Little Valentine liked playing with binary trees very much. Her favorite game was constructing randomly looking binary trees with capital letters in the nodes. 
This is an example of one of her creations: 
                                               D

                                              / \

                                             /   \

                                            B     E

                                           / \     \

                                          /   \     \

                                         A     C     G

                                                    /

                                                   /

                                                  F


To record her trees for future generations, she wrote down two strings for each tree: a preorder traversal (root, left subtree, right subtree) and an inorder traversal (left subtree, root, right subtree). For the tree drawn above the preorder traversal is DBACEGF and the inorder traversal is ABCDEFG. 
She thought that such a pair of strings would give enough information to reconstruct the tree later (but she never tried it). 

Now, years later, looking again at the strings, she realized that reconstructing the trees was indeed possible, but only because she never had used the same letter twice in the same tree. 
However, doing the reconstruction by hand, soon turned out to be tedious. 
So now she asks you to write a program that does the job for her! 

Input

The input will contain one or more test cases. 
Each test case consists of one line containing two strings preord and inord, representing the preorder traversal and inorder traversal of a binary tree. Both strings consist of unique capital letters. (Thus they are not longer than 26 characters.) 
Input is terminated by end of file. 

Output

For each test case, recover Valentine's binary tree and print one line containing the tree's postorder traversal (left subtree, right subtree, root).

Sample Input

DBACEGF ABCDEFG
BCAD CBAD

Sample Output

ACBFGED
CDAB
这道题的题意很清晰,就是给定一棵二叉树的先序和中序遍历的结果,要求这棵二叉树后序遍历的结果。

那么我们根据以上二叉树遍历的几种情况描述进行分析:

首先我们定义两个字符数组分别存储二叉树的先序和中序遍历的结果:

const int maxn = 30;
char pre_str[maxn],in_str[maxn];
然后我们看先序遍历的结果,先序遍历是从根节点开始遍历的,那么我们可以从先序遍历的结果得到整棵树的根节点为pre_str[0],而后我们看中序遍历,中序遍历的遍历过程是左子树 → 根节点 → 右子树,那么我们根据先序遍历得到的整棵树的根节点,然后寻找根节点在中序遍历中的位置,那么在根节点左边的部分则是二叉树的左子树部分,右边的部分则是二叉树的右子树部分。我们可以声明两个字符数组将左右子树存储起来:

char left_tree[maxn],right_tree[maxn];   //分别用来存储"当前根"的左子树和右子树
那么找到了二叉树的左右子树部分之后,我们要开始确定树根的下一层,即此时根节点的左孩子和右孩子节点。我们在前面已经存好了整棵树的左右子树部分,那么为了方便我们首先建立起整棵树的左子树部分,也就是开始寻找根节点的左孩子节点。同样的,根节点的左孩子节点以下部分依旧是一棵以该节点为"根"的二叉树,那么我们依然可以按照确定主根节点的方式确定此时主根节点的左孩子节点:

root->lchild = Build_tree(pre_str,left_tree,root->lchild);    //pre_str表示删除当前已经读取的"根"节点后保存的字符串
                                                              //left_tree表示当前"根"的左子树部分
然后重复当前动作,直到当前"根"左子树部分为空,那么再按照同样的方式寻找其右子树部分。

root->rchild = Build_tree(pre_str,right_tree,root->rchild);
如果当前"根"的左右子树部分均为空,那么表示当前"根"节点处理完毕,那么函数返回上一层,并带回当前"根"的地址,赋值给上一层的"根"节点的左孩子或者右孩子,这样的话,一层一层的"根"节点相互之间就有了联系,当整棵二叉树的左子树部分处理完毕之后,然后再按照同样的方式处理右子树部分,同样的一层一层遍历过去,从底层返回时一层一层的节点建立起联系。当右子树部分处理完毕之后,这样的话,表示我们已经把一棵完整的二叉树构建起来了。然后再将主"根"(即整棵二叉树的根)的地址返回主函数,调用后序遍历函数即可得出结果。

完整代码实现(C语言):

/*思路:先根据前序和中序遍历将整棵树一步步的建立起来
 *然后再调用后序遍历函数输出相应结果即可
 */
 /**
0MS
388KB
user: sunshine
time: 2016-03-09 20:28:59
*/
#include<cstdio>
#include<algorithm>
#include<cstdlib>
#include<cstring>
using namespace std;
typedef struct Bintree_node Node;
const int maxn = 30;
struct Bintree_node{
    Node *lchild,*rchild;  //定义二叉树的节点存储类型
    char data;
};
Node *Init(Node *root,char _data){
    root = (Node *)malloc(sizeof(Node));
    root -> data = _data;
    root -> lchild = NULL;
    root -> rchild = NULL;
    return root;
}
Node *Build_tree(char pre_str[],char in_str[],Node *root){
    char left_tree[maxn],right_tree[maxn];   //分别用来存储"当前根"的左子树和右子树
    root = Init(root,pre_str[0]);
    for(int i = 0;pre_str[i]!='\0';i++){
        pre_str[i] = pre_str[i+1];
    }
    int i = 0,j = 0;
    bool tmp = false;
    while(in_str[i]!='\0'){
        if(tmp){
            right_tree[j] = in_str[i];
            j++;
            i++;
        }
        else if(in_str[i] != root->data && !tmp){
             left_tree[i] = in_str[i];
             i++;
        }
        else{
            tmp = true;
            left_tree[i] = '\0';
            i++;
        }
    }
    right_tree[j] = '\0';
    int len1 = strlen(left_tree),len2 = strlen(right_tree);
    if(len1 > 0){
        root->lchild = Build_tree(pre_str,left_tree,root->lchild);    //pre_str表示删除当前已经读取的"根"节点后保存的字符串
                                                                      //left_tree表示当前"根"的左子树部分
    }
    if(len2 > 0){
        root->rchild = Build_tree(pre_str,right_tree,root->rchild);
    }
    return root;
}
void postorder(Node *node){    //后序遍历
    if(node -> lchild){
        postorder(node -> lchild);
    }
    if(node -> rchild){
        postorder(node -> rchild);
    }
    printf("%c",node -> data);
}
int main(){
    Node *root;
    char pre_str[maxn],in_str[maxn];
    while(scanf("%s %s",pre_str,in_str)==2){
        root = Build_tree(pre_str,in_str,root);
        postorder(root);
        printf("\n");
    }
    return 0;
}
完整代码实现(C++语言):
/**
360MS
21192KB
user: sunshine
time: 2016-03-09 20:24:07
不明白为何同样做法,与C语言耗时和内存消耗相差甚大= =
*/
#include<iostream>
#include<string>
using namespace std;
class Node {
public:
    char data;
    Node *lchild, *rchild;
    Node(char _data) {
        data = _data;
        lchild = NULL;
        rchild = NULL;
    }
    ~Node() {
        if (lchild != NULL) {
            delete lchild;
        }
        if (rchild != NULL) {
            delete rchild;
        }
    }
    void postorder() {
        if (lchild != NULL) {
            lchild->postorder();
        }
        if (rchild != NULL) {
            rchild->postorder();
        }
        cout << data;
    }
    Node *build(const string &pre_str,const string &in_str,int len){
       Node *p = new Node(pre_str[0]);    //存储根节点
       int pos = in_str.find(pre_str[0]);      //寻找根节点在中序遍历中的位置
       if(pos > 0){
            p->lchild = build(pre_str.substr(1,pos),in_str.substr(0,pos),pos);
       }
       if(len - pos - 1 > 0){
            p->rchild = build(pre_str.substr(pos+1),in_str.substr(pos+1),len-pos-1);
       }
       return p;
    }
};
class BinaryTree {
private:
    Node *root;
public:
    BinaryTree() {
        root = NULL;
    }
    ~BinaryTree() {
        if (root != NULL) {
            delete root;
        }
    }
    BinaryTree(const string &pre_str,const string &in_str,int len){
        root = root -> build(pre_str,in_str,len);
    }
    void postorder() {
        root->postorder();
    }
};
int main() {
    string pre_str,in_str;
    while(1){
        getline(cin,pre_str, ' ');      //以' '作为终止符
        getline(cin,in_str);
        BinaryTree binarytree(pre_str,in_str,in_str.length());
        binarytree.postorder();
        cout << endl;
    }
    return 0;
}

小结:二叉树属于数据结构中的重点部分,而二叉树的遍历又是二叉树最基础的部分,可能暂时理解的不是很深入,但是只要坚持下去,多思考,多总结,终归会有提高的。


如有错误,还请指正,O(∩_∩)O谢谢


  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值