前言 :这是一道很简单的题目对吧,但本蒟蒻还是想写写去帮助那些不会的人。
介绍一下树的几种遍历
先序遍历
先序遍历(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;
}
图解过程和之前的差不多,这里就不再赘述了(其实理解上一个的,这一个也就没问题了)。