您的位置:首页 > 职场人生

剑指Offer算法题之已知两种遍历方式重建二叉树--面试题6:重建二叉树

2017-07-31 22:10 411 查看
直接上题目



本题所构建的二叉树如下图



-本文已知二叉树的前序遍历和中序遍历,在二叉树的前序遍历序列中,第一个数字总是树的根结点的值。但在中序遍历序列中,根结点的值再序列的中间,所以 左子树的结点的值位于根结点的值得左边,而右子树的结点的值位于根结点的值得右边。因此可知,我们需要扫描一遍中序遍历,找到根结点。

如下图可知,前序遍历序列的第一个数字1就是根结点,进过扫描中序遍历后找到根结点1所在位置。因此前面3个数字就是左子树结点值,位于1后面的数字都是右子树结点的值。

由于在中序遍历序列中,有3个数字是左子树结点的值,因此左子树总共有3个左子树结点。同样,在前序遍历的根结点后,也应该有3个结点从属于左子树,后面所有的结点从属于右子树。这样就分别找到了左右子树对应的子序列。



既然我们已经找到了中序遍历和前序遍历的左右子树,我们可以用相同的方法分别取构建左右子树。简单说就是用递归的方法去完整整个二叉树的重建。

代码编写思路与注意点:

1、根据题干可得,本题是通过前序遍历和中序遍历重建二叉树的。因此怎么由前序遍历和中序遍历获得后序遍历是关键。–根结点,是本题的突破口。在中序遍历序列中找到根结点是算法的第一步。

2、因为题干的要求是输出根结点,无疑需要为根结点申请空间

3、找到根结点的中序遍历,很清晰得出左子树和右子树的长度。通过这个长度也得出左子树和右子树的前序遍历。为了避免错误,可以列出每个子树头尾结点的位置。

4、注意一个地方:尾结点=头结点+长度-1;,可以举例证实。

5、使用递归继续求得左子树和右子树的根结点,并连接成二叉树。注意:需要判断左子树和右子树是否存在。

6、注意特殊情况:1)当二叉树就只有一个结点时,直接输出根即可 2)当输入两个遍历出现问题时,直接报错:throw exception(“Invalid input”);

7、非常重要,因为输入案例是:长度,前序和中序,而算法的形参是指针,因此需要重写一个模块,把输入数据转化为实参传入重建二叉树模块。

8、根据重建的二叉树,按照每种遍历规则,输出序列。

完整代码如下

#include "stdafx.h"
#include <iostream>

using namespace std;
//构建二叉树的结点
struct BinaryTreeNode
{
int value;
BinaryTreeNode* pLeft;
BinaryTreeNode* pRight;
};

BinaryTreeNode* Construct(int* preorder,int* inorder,int length);
//本题二叉树的核心算法

BinaryTreeNode* ConstructCore(int* startPreorder,int* endPreorder,int* startInorder,int* endInorder)
{
//前序遍历的第一个结点就是根结点
int rootValue =startPreorder[0];
BinaryTreeNode* root=new BinaryTreeNode();
//赋值,注意给左右孩子指针初始化
root->value=rootValue;
root->pLeft = root->pRight = NULL;//左子树和右子树需要初始化
//先处理特殊情况,也就是树只剩一个结点的情况
if(startPreorder==endPreorder)
{
if (startInorder==endInorder)
return root;
else
throw exception("Invalid input");
}
//在中序遍历序列中找到根结点的值
int* rootInorder= startInorder;
while(*rootInorder!=rootValue&&rootInorder<=endInorder)
{
rootInorder++;
}

//找到根结点位置后,分别做出左右子树,及长度
int leftLength=rootInorder-startInorder;
//int rightLength=endInorder-rootInorder;
//左子树的中序遍历
int* leftstartInorder=startInorder;
int* leftendInorder=rootInorder-1;
//右子树的中序遍历
int* rightstartInorder=rootInorder+1;
int* rightendInorder=endInorder;
//左子树的前序遍历
int* leftstartPreorder=startPreorder+1;
int* leftendPreorder=startPreorder+leftLength;//注意
//右子树的前序遍历
int* rightstartPreorder=leftendPreorder+1;
int* rightendPreorder=endPreorder;
//构建左子树--左子树长度>0
if(leftLength>0)
{
root->pLeft=ConstructCore(leftstartPreorder,leftendPreorder,leftstartInorder,leftendInorder);

}
//构建右子树--
if((endInorder-rootInorder)>0)
{
root->pRight=ConstructCore(rightstartPreorder,rightendPreorder,rightstartInorder,rightendInorder);
}
return root;
}

//由输入案例转化到核心算法的模块
BinaryTreeNode* Construct(int* preorder,int* inorder,int length)
{
if(preorder==NULL||inorder==NULL||length<=0)
return NULL;
else
return ConstructCore(preorder,preorder+length-1,inorder,inorder+length-1);
}
//二叉树的前序遍历
void PreorderTraverse(BinaryTreeNode* pNode)
{
if(pNode==NULL)
return ;

cout<<pNode->value<<" ";
PreorderTraverse(pNode->pLeft);
PreorderTraverse(pNode->pRight);

}
//中序遍历
void InorderTraverse(BinaryTreeNode* pNode)
{
if(pNode==NULL)
return ;

PreorderTraverse(pNode->pLeft);
cout<<pNode->value<<" ";
PreorderTraverse(pNode->pRight);

}
//后序遍历
void LastorderTraverse(BinaryTreeNode* pNode)
{
if(pNode==NULL)
return ;

PreorderTraverse(pNode->pLeft);
PreorderTraverse(pNode->pRight);
cout<<pNode->value<<" ";

}

//调用测试模块
void Test(int* preorder,int* inorder,int length)
{
BinaryTreeNode* root=Construct(preorder,inorder,length);

printf("The preorder sequence is: \n");
PreorderTraverse(root);
cout<<endl;

printf("The inorder sequence is:\n ");
InorderTraverse(root);
cout<<endl;

printf("The Lastorder sequence is:\n ");
LastorderTraverse(root);
cout<<endl;
}

//输入测试案例
void Test1()
{
const int length =8;
int preorder[length]={1,2,4,7,3,5,6,8};
int inorder[length]={4,7,2,1,5,3,8,6};

Test(preorder,inorder,length);
}
// 普通二叉树
//              1
//           /     \
//          2       3
//         /       / \
//        4       5   6
//         \         /
//          7       8
int main()
{
Test1();

system("pause");
return 0;
}


测试结果如下:

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
相关文章推荐