根据先序/后序、中序遍历得到后序/先序遍历并按层打印树

两个遍历确定一棵树其中必须有一个是中序遍历


现在有一个问题,已知二叉树的前序遍历和中序遍历:
PreOrder:          GDAFEMHZ
InOrder:            ADEFGHMZ
我们如何还原这颗二叉树,并求出他的后序遍历?

我们基于一个事实:中序遍历一定是 { 左子树中的节点集合 },root,{ 右子树中的节点集合 },前序遍历的作用就是找到每颗子树的root位置。

输入:前序遍历,中序遍历
1、寻找树的root,前序遍历的第一节点G就是root。
2、观察前序遍历GDAFEMHZ,知道了G是root,剩下的节点必然在root的左或右子树中的节点。
3、观察中序遍历ADEFGHMZ。其中root节点G左侧的ADEF必然是root的左子树中的节点,G右侧的HMZ必然是root的右子树中的节点,root不在中序遍历的末尾或开始就说明根节点的两颗子树都不为空。
4、观察左子树ADEF,按照前序遍历的顺序来排序为DAFE,因此左子树的根节点为D,并且A是左子树的左子树中的节点,EF是左子树的右子树中的节点。
5、同样的道理,观察右子树节点HMZ,前序为MHZ,因此右子树的根节点为M,左子节点H,右子节点Z。

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


接着借助一个二维数组,和一个height变量(记录当前所在的层数)就可以在递归的过程中按层把树存起来了。

同理,由后序遍历,中序遍历得到前序遍历和按层输出的方法思路一样,过程如下图:

代码如下:

#include<bits/stdc++.h>
using namespace std;

/*struct TreeNode
{
	char val;
	TreeNode *left,*right;
	TreeNode(char c=' '):
		val(c),left(nullptr),right(nullptr){} 
};*/

void getPostorder(const string&,const string&,string &,int);
void getPreorder(const string &,const string &,string &,int );
void getLevel(const string&,const string&,int,vector<vector<char>>&,int);
string Preorder="GDAFEMHZ";
string Inorder="ADEFGHMZ";

int main()
{
	string Postorder="";
	string new_Pre="";
	getPostorder(Preorder,Inorder,Postorder,int(Preorder.size()));
	getPreorder(Postorder,Inorder,new_Pre,int(Inorder.size()));
	if(Preorder==new_Pre)
		cout<<"Pre: \n"<<new_Pre<<endl;
	else
		cout<<"wrong~"<<endl;

	vector<vector<char>>level;
	getLevel(Preorder,Inorder,int(Preorder.size()),level,0);
	for(auto i:level){
		for(auto j:i)
			cout<<j<<" ";
		cout<<endl;
	}
	return 0;
}
//根据先序,中序,得出后序。这里可以一并完成按层存储的过程,但是由于参数过多,方便理解将两个分为两个函数。
void getPostorder(const string &Pre,const string &In,string &Pos,int len)
{
	if(len==0)
		return;
	char c=Pre[0];
	//在中序遍历中找根节点
	int root_index=0;
	for(;root_index<len;++root_index){
		if(In[root_index]==c)
			break;
	}
	//左子树
	getPostorder(Pre.substr(1),In,Pos,root_index);
	//右子树
	getPostorder(Pre.substr(root_index+1),In.substr(root_index+1),Pos,len-root_index-1);
	Pos.insert(Pos.end(),c);
	return;
}
//根据后序,中序,得出先序
void getPreorder(const string &Post,const string &In,string &Pre,int len)
{
	if(len==0)
		return;
	char c=Post[Post.size()-1];
	//在中序中寻找根节点
	int root_index=0;
	for(;root_index<len;++root_index){
		if(In[root_index]==c)
			break;
	}
	//先序,就先执行插入字符的操作
	Pre.insert(Pre.end(),c);
	//左子树
	getPreorder(Post.substr(0,root_index),In,Pre,root_index);
	//右子树
	getPreorder(Post.substr(root_index,len-root_index-1),In.substr(root_index+1),
		Pre,len-root_index-1);
	return;
}
//根据先序,中序,按层打印出树,按后序和中序就不再重复了 
void getLevel(const string &Pre,const string &In,int len,vector<vector<char>>&level,int height)
{
	if(len==0)
		return;
	char c=Pre[0];
	if(height>=level.size())
		level.emplace_back(vector<char>());
	level[height++].emplace_back(c);
	//在中序遍历中找根节点
	int root_index=0;
	for(;root_index<len;++root_index){
		if(In[root_index]==c)
			break;
	}
		//左子树
	getLevel(Pre.substr(1),In,root_index,level,height);
	//右子树
	getLevel(Pre.substr(root_index+1),In.substr(root_index+1),len-root_index-1,level,height);
	return;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
二叉的二叉链表存储结构定义如下: ```c typedef struct BiTNode { int data; struct BiTNode *lchild, *rchild; } BiTNode, *BiTree; ``` 其,data 表示结点的数据项,lchild 和 rchild 分别指向左右子。 二叉的创建可以采用递归方式,先输入结点,然后递归输入左右子。 ```c void createBiTree(BiTree *T) { int data; scanf("%d", &data); if (data == -1) { *T = NULL; } else { *T = (BiTree) malloc(sizeof(BiTNode)); (*T)->data = data; createBiTree(&((*T)->lchild)); createBiTree(&((*T)->rchild)); } } ``` 二叉先序/序/后序递归遍历可以采用类似的递归方式,分别先遍历结点,然后递归遍历左右子。 ```c // 先序遍历 void preOrder(BiTree T) { if (T != NULL) { printf("%d ", T->data); preOrder(T->lchild); preOrder(T->rchild); } } // 中序遍历 void inOrder(BiTree T) { if (T != NULL) { inOrder(T->lchild); printf("%d ", T->data); inOrder(T->rchild); } } // 后序遍历 void postOrder(BiTree T) { if (T != NULL) { postOrder(T->lchild); postOrder(T->rchild); printf("%d ", T->data); } } ``` 统计二叉的高度可以采用递归方式,左右子的高度取最大值并加 1。 ```c int getDepth(BiTree T) { if (T == NULL) { return 0; } else { int leftDepth = getDepth(T->lchild); int rightDepth = getDepth(T->rchild); return (leftDepth > rightDepth ? leftDepth : rightDepth) + 1; } } ``` 统计各类结点的个数同样可以采用递归方式,分别统计结点、左子、右子的结点个数。 ```c int getNodeNum(BiTree T) { if (T == NULL) { return 0; } else { return getNodeNum(T->lchild) + getNodeNum(T->rchild) + 1; } } int getLeafNum(BiTree T) { if (T == NULL) { return 0; } else if (T->lchild == NULL && T->rchild == NULL) { return 1; } else { return getLeafNum(T->lchild) + getLeafNum(T->rchild); } } int getSingleNum(BiTree T) { if (T == NULL) { return 0; } else if ((T->lchild == NULL && T->rchild != NULL) || (T->lchild != NULL && T->rchild == NULL)) { return getSingleNum(T->lchild) + getSingleNum(T->rchild) + 1; } else { return getSingleNum(T->lchild) + getSingleNum(T->rchild); } } ``` 先序/序非递归遍历采用栈来实现。先序遍历时,先将结点入栈,然后弹出栈顶结点并输出其值,如果该结点有右子,则将其右子入栈;如果该结点有左子,则将其左子入栈。中序遍历时,先将结点入栈,然后将其左子全部入栈,再弹出栈顶结点并输出其值,最后将其右子入栈。 ```c // 先序递归遍历 void preOrderNonRecursive(BiTree T) { if (T == NULL) { return; } BiTree stack[MAXSIZE]; int top = -1; stack[++top] = T; while (top != -1) { BiTree node = stack[top--]; printf("%d ", node->data); if (node->rchild != NULL) { stack[++top] = node->rchild; } if (node->lchild != NULL) { stack[++top] = node->lchild; } } } // 序非递归遍历 void inOrderNonRecursive(BiTree T) { if (T == NULL) { return; } BiTree stack[MAXSIZE]; int top = -1; BiTree node = T; while (node != NULL || top != -1) { while (node != NULL) { stack[++top] = node; node = node->lchild; } if (top != -1) { node = stack[top--]; printf("%d ", node->data); node = node->rchild; } } } ``` 层序遍历采用队列来实现。先将结点入队,然后每次弹出队首结点并输出其值,如果该结点有左子,则将其左子入队;如果该结点有右子,则将其右子入队。 ```c void levelOrder(BiTree T) { if (T == NULL) { return; } BiTree queue[MAXSIZE]; int front = 0, rear = 0; queue[rear++] = T; while (front != rear) { BiTree node = queue[front++]; printf("%d ", node->data); if (node->lchild != NULL) { queue[rear++] = node->lchild; } if (node->rchild != NULL) { queue[rear++] = node->rchild; } } } ```

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值