[数据结构] 由 二叉树 “前中序/中后序 “遍历所思所想

有一段时间没学习数据结构了,趁着开学前把树,图相关的知识搞一遍,下学期就主攻算法了

我希望按自己的理解记录这个过程, 也算是督促自己


一.为什么不能是前后序❓ 

可以唯一确定一棵二叉树的遍历序列组合

  • 已知前序和中序的遍历序列
  • 已知中序和后序的遍历序列

❓为什么已知已知前序和后序的遍历序列无法唯一确定一棵二叉树呢?

因为即使可以确定层级关系,也无法得知哪个是左子树哪个是右子树

👇例如:       已知前序序列是ABC        后序序列是CBA

两个任取一个都可以看出A是根节点,由后序遍历可知B是A的孩子,C是B的孩子,但左右子树不唯一

该例中可能的情况有四种,

二.人工推导

前中序

 例一:已知👉 前序遍历序列 ABCDEF ,中序遍历序列 CBAEDF ,求后续遍历的结果

首先要清楚前中后遍历的规则,在此不再赘述

其次体会他们各自的特点及相互之间的微妙联系

  • (前序遍历结果中各个"区域"的第一个必是该区域的根)
  • (后序遍历结果中各个"区域"的最后一个必是该区域的根)

在这道题中,由前序遍历可知A为整棵树的根节点,👉再看中序遍历里面A的左子树(下划线部分)有CBAEDF, 再由前序遍历的BC顺序可知B是C的父亲,且C是B的左孩子(确定了父节点B后,中序遍历中的排列顺序可以体现出左右子树的关系), DEF同理,这棵二叉树长这样👇

后序遍历结果为CBEFDA

中后序(与前中序大体相似)

 例二:已知👉 中序遍历序列 ABCDEFG,后序遍历序列 BDCAFGE,求前序遍历的结果

 与上面的例子大相径庭,由后序遍历的特点,E为整棵树的根,👉再看中序遍历里面E的左子树(下划线部分)有ABCDEFG, 再由后序遍历的"BDCA"顺序可知A是这棵子树的根(即E的左孩子),"BCD"为A的右子孙,由后序遍历的"BDC"顺序可知C为这棵子树的根(即A的右孩子),在前序遍历中''A(BCD)EFG'',所以B,C分别为C的左右孩子,E的右子树同理,树长这样👇

前序遍历结果为EACBDGF

三.代码实现

HDU 1710 Binary Tree Traversals为例

#include <stdio.h>

const int N = 1010;
int pre[N], in[N], post[N];  //先序、中序、后序
int k;
struct node{
    int value;
    node *l, *r;
    node(int value = 0, node *l = NULL, node *r = NULL):value(value), l(l), r(r){}
};

void buildtree(int l, int r, int &t, node* &root) { //建树
    int flag = -1;
    for(int i = l; i <= r; i++) //先序的第一个是根,找到对应的中序的位置
        if(in[i] == pre[t]){
            flag = i; 
			break;
        }
    if(flag == -1) return;       //结束
    root = new node(in[flag]);   //新建结点
    t++;
    if(flag > l)  buildtree(l, flag - 1, t, root ->l);
    if(flag < r)  buildtree(flag + 1, r, t, root ->r);
}

void preorder (node *root){       //求先序序列
    if(root != NULL){
        post[k++] = root ->value;  //输出
        preorder (root ->l);
        preorder (root ->r);
    }
}

void inorder (node *root){        //求中序序列
    if(root != NULL){
        inorder (root ->l);
        post[k++] = root ->value;  //输出
        inorder (root ->r);
    }
}

void postorder (node *root){      //求后序序列
    if(root != NULL){
        postorder (root ->l);
        postorder (root ->r);
        post[k++] = root ->value;  //输出
    }
}

void remove_tree(node *root){      //释放空间
    if(root == NULL) return;
    remove_tree(root->l);
    remove_tree(root->r);
    delete root;
}

int main(){
    int n;
    while(~scanf("%d", &n)){
        for(int i=1;i<=n;i++) scanf("%d", &pre[i]);
        for(int j=1;j<=n;j++) scanf("%d", &in[j]);
        node *root;
        int t = 1;
        buildtree(1, n, t, root);
        k = 0;           //记录结点个数
        postorder(root);
        for(int i=0;i<k;i++) printf("%d%c",post[i],i==k-1?'\n':' ');
          //作为验证,这里可以用preorder()和inorder()检查先序和中序遍历
        remove_tree(root);
    }
    return 0;
}

其实单就求另一个序列的遍历的话,可以不建树,明天再补充点吧....

posted @ 2021-08-14 23:40  泥烟  阅读(35)  评论(0编辑  收藏  举报