输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建二叉树并返回。
#以下完整代码,读者可将其直接复制到IDE里运行查看,效果更佳。此处不多bibi。
/* 根据先序序列和中序序列创建二叉树。 */
#include
#include
#include
#include
using namespace std;
struct TreeNode {
int val;
TreeNode *left;
TreeNode *right;
TreeNode(int x) : val(x), left(NULL), right(NULL) {}
};
/* 先序中拿出第一个节点为根节点,然后在中序序列中找到此节点,此节点的左边为它的
* 左子树,右边为它的右子树。*/
class Solution {
public:
TreeNode* reConstructBinaryTree(vector<int> pre,vector<int> vin) {
//最终所得树的根节点
TreeNode* root = NULL;
//重建二叉树
//root = ReCreate_BiTree(root,pre,vin);
ReCreate_BiTree(root,pre,vin);
return root;
}
//递归重建二叉树
TreeNode* ReCreate_BiTree(TreeNode* &root,vector<int> pre,vector<int> vin)//引用传递
{
int i = 0;
bool pre_root = true;
//定义四个int容器来存放左右子树的先序和中序序列。
vector<int> pre_r,pre_l,vin_r,vin_l;
//如果序列为空则说明此节点为空。
if(pre.size() == 0 || vin.size() == 0)
{
root = NULL;
return NULL;
}
//找到中序序列中的根,提取左边和右边。分别为左子树的先序和中序。
for(i=0; i<pre.size(); i++)
{
//如果是根节点则将标志位置为false,且退出本次循环
if(vin[i] == pre[0]){
pre_root = false;
//break代表退出整个for循环
//此处应该用continue
continue;
}
//中序序列中,根节点前面为左子树的中序序列。
else if(pre_root)
vin_l.push_back(vin[i]);
//中序序列中,根节点后面为右子树的中序序列。
else
{
vin_r.push_back(vin[i]);
}
}
//左子树的中序序列个数和先序序列个数相同,则先序序列中,
//根节点后长度为左子树的中序序列长度的元素为左子树的先序序列。
//剩下的则为右子树的先序序列。
for(i=1;i<pre.size();i++)
{
//根节点后vin_l.size()多个元素为左子树先序序列的元素。
if(i<=vin_l.size())
pre_l.push_back(pre[i]);
//否则则是右子树先序序列的元素。
else
pre_r.push_back(pre[i]);
}
/* !!!注意此处 root的指针不再和调用它的root指向同一个地方 */
root = (TreeNode*)malloc(sizeof(TreeNode));
root->val = pre[0];
ReCreate_BiTree((root->left),pre_l,vin_l);
ReCreate_BiTree((root->right),pre_r,vin_r);
return root;
}
};
/* 上述算法的简单处理,将递归函数加个参数n表示先序或中序序列长度。
* 然后就不必申请一堆容器去存储,只需传递的时候做相应处理即可。
* 但是再在处理的时候,发现容器不能进行筛选。所以此处采用将容器转化成数组型进行操作。*/
class Solution_array {
public:
TreeNode* reConstructBinaryTree(vector<int> pre,vector<int> vin) {
//最终所得树的根节点
TreeNode* root = NULL;
//重建二叉树
int *pre_array = &pre[0],*vin_array = &vin[0];
ReCreate_BiTree(root,pre_array,vin_array,pre.size());
return root;
}
//递归重建二叉树
void ReCreate_BiTree(TreeNode* &root,int *pre,int *vin,int n)//引用传递
{
int i = 0;
//如果序列为空则说明此节点为空。
if(n == 0)
{
root = NULL;
return;
}
//根据先序的第一个来确定中序中根节点的位置。 中序中第i个为根
while(pre[0] != vin[i++]&&i<n);
root = (TreeNode*)malloc(sizeof(TreeNode));
root->val = pre[0];
//递归创建相应的左子树和右子树
//三个参数分别为根节点的右子节点指针域,和右子树的先序和中序
ReCreate_BiTree(root->left,pre+1,vin,i-1);
//三个参数分别为根节点的右子节点指针域,和右子树的先序和中序
ReCreate_BiTree(root->right,pre+i,vin+i,n-i);
}
};
static void printTree(TreeNode* root,int h)
{
//printf("printTree open\n");
//printf("%d\n",h); /* 输出节点 */
if(root == NULL)
return;
printTree(root->right,h+1); /* 先打印右子树 */
for(int i=0;i<h;i++) /* 深度决定节点的左右位置 */
printf(" "); /* 打印空格来缺点位置 */
printf("%d\n",root->val); /* 输出节点 */
printTree(root->left,h+1); /* 后打印左子树 */
}
/* 前序遍历二叉树 */
static void pre_print(const TreeNode* root)
{
if(!root)
return;
/* 访问节点 */
printf("%d ",root->val);
/************/
pre_print(root->left);
pre_print(root->right);
}
/* 中序遍历二叉树 */
static void vin_print(const TreeNode* root)
{
if(!root)
return;
vin_print(root->left);
/* 访问节点 */
printf("%d ",root->val);
/************/
vin_print(root->right);
}
/* 后序遍历二叉树 */
static void back_print(const TreeNode* root)
{
if(!root)
return;
back_print(root->left);
back_print(root->right);
/* 访问节点 */
printf("%d ",root->val);
/************/
}
/* 先序遍历非递归实现 */
/* 首先访问根节点并保存根节点(为了以后根据此节点找到),
* 找到左节点然后访问并保存。直到左子树为空,
* 然后从栈里弹出最后一个左节点的父亲节点,
* 然后根据此节点访问其右子节点。直到栈空 */
static void pre_N_rec(TreeNode* root)
{
stack<TreeNode*> stk;
/* 如果栈非空,则出栈并访问其右节点
* 如果root不等于空,则访问root节点,并将其入栈 */
//int i = 0;
while(root!=NULL||!stk.empty())
{
//cout<
/* 如果root不等于空,则访问root节点,并将其入栈 */
while(root)
{
/* 访问根节点 */
printf("%d ",root->val);
/**************/
/* 并将根节点入栈 */
stk.push(root);
/* 先访问其左节点 */
root = root->left;
}
/* 如果栈非空且当前节点为空,则将其父节点出栈并访问其右节点 */
if(!stk.empty())
{
TreeNode *top = stk.top();
stk.pop();
root = top->right;
}
}
}
/* 中序遍历非递归
* 与先序遍历类似,只不过把访问节点放在出栈时访问 */
static void inv_N_rec(TreeNode* root)
{
stack<TreeNode*> stk;
/* 如果栈非空,则出栈并访问其根节点
* 如果root不等于空,则将其入栈 */
//int i = 0;
while(root!=NULL||!stk.empty())
{
//cout<
/* 如果root不等于空,则访问root节点,并将其入栈 */
while(root)
{
/* 将根节点入栈 */
stk.push(root);
/* 先访问其左节点 */
root = root->left;
}
/* 如果栈非空且当前节点为空,则将其父节点出栈并访问其右节点 */
if(!stk.empty())
{
TreeNode *top = stk.top();
stk.pop();
/* 访问根节点 */
printf("%d ",top->val);
/**************/
/* 访问右节点 */
root = top->right;
}
}
}
/* 后序遍历非递归
* 与中序先序不同的是后序,在先序、中序遍历算法中,从左子树返回时,上层节点先退栈
* 再访问右子树。而后序遍历中,左右子树均访问完成后,从右子树返回时,上层节点才能
* 退栈并返回。
* 所以申请一个变量LastVisitedNode用来存放上次访问的是右子节点还是左子节点,
* 还有一种情况是,右子树为空时,直接退栈并访问它。 */
static void back_N_rec(TreeNode* root)
{
stack<TreeNode*> stk;
TreeNode *LastVisitedNode;//用来区分是左子节点返回还是右子节点返回。
/* 如果栈非空,则出栈并访问其右节点
* 如果root不等于空,则访问root节点,并将其入栈 */
//int i = 0;
while(root!=NULL||!stk.empty())
{
//cout<
/* 当root不等于空,则访问root节点,并将其入栈 */
while(root)
{
/* 将根节点入栈 */
stk.push(root);
/* 先访问其左节点 */
root = root->left;
}
/* 如果栈非空且当前节点为空 */
if(!stk.empty())
{
TreeNode *top = stk.top();
/* 判断栈顶结点top的右子树是否为空或者右子树是否刚被访问过,是,则退栈。 */
if((top->right == NULL)||(top->right == LastVisitedNode))
{
stk.pop();
/* 访问根节点 */
printf("%d ",top->val);
/**************/
/* 保存访问的节点以便判断是从右还是从左节点返回。 */
LastVisitedNode = top;
}
/* 不是,则进入右子树 */
else
root = top->right;
}
}
}
int main()
{
const int a[] = {18,14,7,3,11,22,35,27};//先序序列
const int b[] = {3,7,11,14,18,22,27,35};//中序序列
vector<int> pre(a,a+8);//先序序列
vector<int> vin(b,b+8);//中序序列
/* 当传入的序列为数组时,可采用此类中的方法 */
Solution_array s1;
/* 当传入的序列为容器时,可采用此类中的方法 */
//Solution s1;
TreeNode* root;
/* 创建二叉树 */
root = s1.reConstructBinaryTree(pre,vin);
//printTree(root,1);
/* 递归遍历 */
/* 先序遍历 */
//pre_print(root) ;//18 14 7 3 11 22 35 27
/* 中序遍历 */
//vin_print(root); //3 7 11 14 18 22 27 35
/* 后序遍历 */
//back_print(root);//3 11 7 14 27 35 22 18
/* 非递归实现 */
/* 先序非递归遍历 */
//pre_N_rec(root) ;//18 14 7 3 11 22 35 27
/* 中序非递归遍历 */
//inv_N_rec(root); //3 7 11 14 18 22 27 35
/* 后序非递归遍历 */
back_N_rec(root); //3 11 7 14 27 35 22 18
}