1. 程式人生 > >根據先序/後序、中序遍歷得到後序/先序遍歷並按層列印樹

根據先序/後序、中序遍歷得到後序/先序遍歷並按層列印樹

兩個遍歷確定一棵樹其中必須有一個是中序遍歷

現在有一個問題,已知二叉樹的前序遍歷和中序遍歷:
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;
}