剑指offer——根据先序,中序重建二叉树C/C++(以及二叉树的三种递归和非递归遍历实现)

题目描述

输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建二叉树并返回。
#以下完整代码,读者可将其直接复制到IDE里运行查看,效果更佳。此处不多bibi。

/* 根据先序序列和中序序列创建二叉树。 */ 
#include <iostream>
#include <vector>
#include <cstdlib>
#include <stack>
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<<i++; 
		/* 如果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<<i++; 
		/* 如果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<<i++; 
		/* 当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 
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值