求二叉树序遍历

前言 :这是一道很简单的题目对吧,但本蒟蒻还是想写写去帮助那些不会的人。

介绍一下树的几种遍历
先序遍历
先序遍历(Pre-order),先序就是按照最优先顺序,遍历就是沿一定路径经过路径上所有的站。在二叉树中,先根后左再右。巧记:根左右。

typedef struct TreeNode {
    int data;
    struct TreeNode *left;
    struct TreeNode *right;
    struct TreeNode *parent;
} TreeNode;
void middle_order(TreeNode *Node) {
    if(Node != NULL) {
        printf("%d ", Node->data);
        middle_order(Node->left);
        middle_order(Node->right);
    }
}

中序遍历
中序遍历(In-order)是二叉树遍历的一种,也叫做中根遍历、中序周游。在二叉树中,先左后根再右。巧记:左根右。

typedef struct TreeNode {
    int data;
    struct TreeNode *left;
    struct TreeNode *right;
    struct TreeNode *parent;
} TreeNode;
void middle_order(TreeNode *Node) {
    if(Node != NULL) {
        middle_order(Node->left);
        printf("%d ", Node->data);
        middle_order(Node->right);
    }
}

后序遍历
后序遍历(Post-order)是二叉树遍历的一种,也叫做后根遍历、后序周游,可记做左右根。后序遍历有递归算法和非递归算法两种。在二叉树中,先左后右再根。巧记:左右根。

typedef struct TreeNode {
    int data;
    struct TreeNode *left;
    struct TreeNode *right;
    struct TreeNode *parent;
} TreeNode;
void middle_order(TreeNode *Node) {
    if(Node != NULL) {
        middle_order(Node->left);
        middle_order(Node->right);
        printf("%d ", Node->data);
    }
}

实际上说白了就是printf的位置的变化:
最前(先序遍历)->中间(中序遍历)->最后(后序遍历)

首先我们要知道我们可以
①知道 中序+先序->后序
②知道 中序+后序->先序

为什么不可以 知道 后序中序求先序呢?
考虑如下图中的几棵二叉树:

这里写图片描述

所有这些二叉树都有着相同的前序遍历和后序遍历,但中序遍历却不相同。

但是知道中序和(后序/先序)就能求(先序/后序)(这里就不做证明了,想想就知道了)


假设有棵树,长下面这个样子,它的前序遍历,中序遍历,后续遍历都很容易知道。
这里写图片描述
PreOrder: GDAFEMHZ
InOrder: ADEFGHMZ
PostOrder: AEFDHZMG


一、已知前序(pre_order)、中序(in_order)遍历,求后序(post_order)遍历
PreOrder: GDAFEMHZ
InOrder: ADEFGHMZ

第一步,根据前序遍历的特点,我们知道根结点为第一位——G

第二步,观察中序遍历ADEFGHMZ。其中根节点G左侧的ADEF必然是他的的左子树根节点G右侧的HMZ必然是他的的右子树

第三步,观察左子树ADEF,左子树的中的根节点必然是他(左子树的中的根节点)父亲的左儿子。在前序遍历中,他父亲的左儿子位于他父亲的后一位,所以左子树的根节点为D。

第四步,同样的道理,根节点的右子树HMZ中的根节点也可以通过前序遍历求得。在前序遍历中,一定是先把根和根的所有左子树节点遍历完之后才会遍历右子树并且遍历的左子树的第一个节点就是左子树的根节点。同理,遍历的右子树的第一个节点就是右子树的根节点

第五步,观察发现,上面的过程是递归的。先找到当前树的根节点,然后划分为左子树,右子树,然后进入左子树重复上面的过程,然后进入右子树重复上面的过程。最后就可以还原一棵树了。该步递归的过程可以简洁表达如下:

1 确定根,确定左子树,确定右子树。

2 在左子树中递归。

3 在右子树中递归。

4 打印当前根。

那么,我们就能求出这棵树的后序遍历了

#include<cstdio>
#include<cstring>
void post_order(char*in_order,char*pre_order,int length){
    if(!length)return;
    int root_index=0;
    for(;root_index<length;root_index++)
        if(in_order[root_index]==*(pre_order))break;
    post_order(in_order,pre_order+1,root_index);
    post_order(in_order+root_index+1,pre_order+root_index+1,length-root_index-1);
    putchar(*(pre_order));
}
int main(){
    char*in_order=new char[1000];
    char*pre_order=new char[1000];
    gets(pre_order);gets(in_order);
    post_order(in_order,pre_order,strlen(in_order));
return 0;
}

(ps: root_index->根指针)
看代码是不是有点迷?如果是我们来一步一步图解一下
这里写图片描述
然后我们在中序遍历中找到根节点
这里写图片描述
因为中序遍历中根节点的左边就是他的左子树,右边就是他的右子树
然后左右子树的大小又是一定的,所以
这里写图片描述
于是我们确定了之后就可以继续向下递归了
然后我们又发现
这里写图片描述
这下我们就知道左右子树的大小(即长度)了

if(!length)return;

长度=0时,说明这个子树已经空了,所以要返回
然后递归下传的指针要怎么变呢?
我们只需要下传起始指针就ok了,因为我们已经知道长度了
我们数格子可以发现
这里写图片描述
于是我们就知道如何下传指针了

    post_order(in_order,pre_order+1,root_index);
    post_order(in_order+root_index+1,pre_order+root_index+1,length-root_index-1);

先遍历左子树,再遍历右子树
最后输出根节点

putchar(*(pre_order));

(觉得指针不好写的,可以改成下标变量也是可以的)

#include<cstdio>
#include<cstring>
char in_order[1000];
char pre_order[1000];
void post_order(int in,int pre,int length){
    if(!length)return;
    int root_index=0;
    for(;root_index<length;root_index++)
        if(in_order[in+root_index]==pre_order[pre])break;
    post_order(in,pre+1,root_index);
    post_order(in+root_index+1,pre+root_index+1,length-root_index-1);
    putchar(pre_order[pre]);
}
int main(){
    gets(pre_order);gets(in_order);
    post_order(0,0,strlen(in_order));
return 0;
}

一、已知后序(post_order)、中序(in_order)遍历,求前序(pre_order)遍历
PostOrder: AEFDHZMG
InOrder: ADEFGHMZ

第一步,根据后序遍历的特点,我们知道根结点为最后一位——G

第二步,观察中序遍历ADEFGHMZ。其中根节点G左侧的ADEF必然是他的的左子树根节点G右侧的HMZ必然是他的的右子树

第三步,观察左子树ADEF,左子树的中的根节点必然是他(左子树的中的根节点)父亲的左儿子。在后序遍历中,他父亲的左子树位于最前面几(左子树的大小)位,即AEFD,根据后序遍历的性质,这棵子树的根节点是这个子树后序遍历的最后一位,所以左子树的根节点为D。

第四步,同样的道理,根节点的右子树HMZ中的根节点也可以通过后序遍历求得。在后序遍历中,一定是先把根的所有左子树节点遍历完之后才会遍历右子树并且遍历的左子树的最后一个节点就是左子树的根节点。同理,遍历的右子树的最后一个节点就是右子树的根节点

第五步,观察发现,上面的过程是递归的。先找到当前树的根节点,然后划分为左子树,右子树,然后进入左子树重复上面的过程,然后进入右子树重复上面的过程。最后就可以还原一棵树了。该步递归的过程可以简洁表达如下:

1 确定根,确定左子树,确定右子树。

2 打印当前根

3 在左子树中递归。

4 在右子树中递归。

那么,我们就能求出这棵树的前序遍历了

#include<cstdio>
#include<cstring>
void pre_order(char*in_order,char*post_order,int length){
    if(!length)return;
    int root_index=0;
    for(;root_index<length;root_index++)
        if(in_order[root_index]==*(post_order+length-1))break;
    putchar(*(post_order+length-1));
    pre_order(in_order,post_order,root_index);
    pre_order(in_order+root_index+1,post_order+root_index,length-root_index-1);
}
int main(){
    char*in_order=new char[1000];
    char*post_order=new char[1000];
    gets(in_order);gets(post_order);
    pre_order(in_order,post_order,strlen(in_order));
return 0;
}

图解过程和之前的差不多,这里就不再赘述了(其实理解上一个的,这一个也就没问题了)。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值