您的位置:首页 > 编程语言 > C语言/C++

根据二叉树的前序遍历和中序遍历,重构出二叉树

2013-07-27 20:07 453 查看
题目:这道题目是一道面试题,先序遍历和中序遍历以数组的形式给出,要求我们根据这两个条件重构出二叉树。

下图是一棵二叉树

//              6
//           /     \
//          5       7
//         / \       \
//        2   4       8


先序遍历:6,5,2,4,7,8

中序遍历:2,5,4,6,7,8

思路:二叉树先序遍历的定义:1,先输出根结点2,再输出左子树3,再输出右子树,因此先序遍历时,根结点总会出现在数组开头,中序遍历时,根结点可以将二叉树分为左右子树。

因此,重构二叉树的步骤可以用自顶向下的方法:

1,先在先序序列中找到根结点,

2,在中序序列中找到根结点位置,(可以将二叉树分为左子树和右子树)

3.用同样的办法构造左子树

4.用同样的办法构造右子树。

例如:

1.找到根结点6,因此左子树是2,5,4和右子树是7,8

2.找到左边根结点5,可将子二叉树分为2和4,因此左边确定。

3,找到右边根结点7,因此可确定右子树8。

根据四步走可以写出如出代码:

node* Build_Tree(int* prec,int* inorder,int len)
{	//步骤1:新建根结点
node* root=new node(prec[0]);
//步骤2:在中序遍历中找到根结点索引,分割左右子树
int SubTreeLen=0;
while(SubTreeLen < len && inorder[SubTreeLen] != prec[0])
++SubTreeLen;

if(SubTreeLen > 0)
{	//步骤2:重建左子树,并且将根结点root的left指向左子树
root->left=Build_Tree(prec+1,inorder,SubTreeLen);
}
if(len-SubTreeLen-1 > 0)
{	//步骤2:重建右子树,并且将根结点root的left指向右子树
root->right=Build_Tree(prec+1+SubTreeLen,inorder+1+SubTreeLen,len-SubTreeLen-1);
}
return root;
}


接下来的步骤就是进行程序测试,利用边界值等去测试程序的健壮性。

可以利用下面的二叉树去测试:

// 普通二叉树
//              1
//           /     \
//          2       3
//         /       / \
//        4       5   6
//         \         /
//          7       8
int preorder[length] = {1, 2, 4, 7, 3, 5, 6, 8};
int inorder[length] = {4, 7, 2, 1, 5, 3, 8, 6};

// 所有结点都没有右子结点
//            1
//           /
//          2
//         /
//        3
//       /
//      4
//     /
//    5

int preorder[length] = {1, 2, 3, 4, 5};
int inorder[length] = {5, 4, 3, 2, 1};

// 所有结点都没有左子结点
//            1
//             \
//              2
//               \
//                3
//                 \
//                  4
//                   \
//                    5
int preorder[length] = {1, 2, 3, 4, 5};
int inorder[length] = {1, 2, 3, 4, 5};

// 树中只有一个结点
int preorder[length] = {1};
int inorder[length] = {1};
// 完全二叉树
//              1
//           /     \
//          2       3
//         / \     / \
//        4   5   6   7
int preorder[length] = {1, 2, 4, 5, 3, 6, 7};
int inorder[length] = {4, 2, 5, 1, 6, 3, 7};

// 输入空指针

// 输入的两个序列不匹配

int preorder[length] = {1, 2, 4, 5, 3, 6, 7};
int inorder[length] = {4, 2, 8, 1, 6, 3, 7};


上述测试例子引自于:《剑指Offer——名企面试官精讲典型编程题》

经过测试,当输入空指针和两个序列不匹配时,原始的程序无办法处理,可以针对性处理如下:

#include<iostream>
#include <exception>
using std::cout;
using std::endl;
struct node
{
int value;
node* left;
node* right;
node(int v):value(v),left(NULL),right(NULL){}
};

node* Build_Tree(int* prec,int* inorder,int len)
{
if(!prec || !inorder || len <=0 )
{
cout<<"Empty Input!"<<endl;
exit(1);//暴力关机
}
//步骤1:新建根结点
node* root=new node(prec[0]);
//步骤2:在中序遍历中找到根结点索引,分割左右子树
int SubTreeLen=0;
while(SubTreeLen < len && inorder[SubTreeLen] != prec[0])
++SubTreeLen;
if(SubTreeLen == len)//越界了,说明两个数组无法构造出二叉树
{
cout<<"Wrong Input!"<<endl;
exit(1);//暴力关机
}
if(SubTreeLen > 0)
{	//步骤2:重建左子树,并且将根结点root的left指向左子树
root->left=Build_Tree(prec+1,inorder,SubTreeLen);
}
if(len-SubTreeLen-1 > 0)
{	//步骤2:重建右子树,并且将根结点root的left指向右子树
root->right=Build_Tree(prec+1+SubTreeLen,inorder+1+SubTreeLen,len-SubTreeLen-1);
}
return root;
}

void prec_tree_walk(node* z)
{
if(!z)
return ;
cout<<z->value<<' ';
prec_tree_walk(z->left);
prec_tree_walk(z->right);
}

void inorder_tree_walk(node* z)
{
if(!z)
return ;
inorder_tree_walk(z->left);
cout<<z->value<<' ';
inorder_tree_walk(z->right);
}
int main()
{
int prec[] = {1, 2, 4, 7, 3, 5, 6, 8};
int Inorder[] = {4, 7, 2, 1, 5, 3, 8, 6};

int len=sizeof(prec)/sizeof(prec[0]);
node* root=Build_Tree(prec,Inorder,len);
prec_tree_walk(root);
cout<<endl;
inorder_tree_walk(root);
return 0;
}

中途发现的编译错误记录如下:点击打开链接
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息