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

面试题五 重建二叉树

2014-03-18 16:16 239 查看
题目

  输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。

分析

  1. 取先序遍历的第一个值为树的根结点

  2. 在中序遍历中找到1中的那个结点

  3. 中序遍历中,处于该结点左侧的所有结点,是该树左子树的中序遍历;处于该结点右侧的所有结点,是该树右子树的中序遍历。

  4. 前序遍历中,左右子树的前序遍历也是分开放的。因此根据3中获得的中序遍历的长度,可以将前序遍历分割为左右子树的前序遍历。

  根据这个思路,就可以写出递归算法/程序了。

代码实现( 含测试 )

#include <iostream>

using namespace std;

// 定义二叉树结点类型
struct BinaryTreeNode {
int value;
BinaryTreeNode * left;
BinaryTreeNode * right;
};

// 构建二叉树函数
BinaryTreeNode * construct( BinaryTreeNode * &T, int *pre, int *in, int len );

// 遍历函数
void preOrderPrint( BinaryTreeNode *T );
void inOrderPrint( BinaryTreeNode *T );
void postOrderPrint( BinaryTreeNode *T );

// 定义最大处理结点数
const int MAX = 1000;

// 定义合法标志
int TAG=0;

int main()
{
int preOrder[MAX];
int inOrder[MAX];

/*
* 获取前序和中序序列。
*/
int n;
cout << "请输入树的结点数( 前/中序遍历的长度 ):" << endl;
cin >> n;
if (n >= MAX) {
cout << "结点数过大!" << endl;
return 1;
}
if (n <= 0) {
cout << "输入结点数异常,无法构造树。" << endl;
return 1;
}

cout << "请输入前序遍历序列:" << endl;
for (int i=0; i<n; i++) {
cin >> preOrder[i];
}

cout << "请输入中序遍历序列:" << endl;
for (int i=0; i<n; i++) {
cin >> inOrder[i];
}

// 初始化根结点,前序遍历起点,中序遍历起点。
BinaryTreeNode * T=NULL;
int *pre = preOrder;
int *in = inOrder;

// 调用构建函数构建二叉树
T = construct( T, preOrder, inOrder, n );

// 如果输入的前序和中序序列不匹配。
if (TAG == 1) {
cout << "前序和中序序列不匹配,构造失败。" << endl;
return 1;
}

// 构建成功,打印此二叉树。
cout << endl << "构建成功,下面输出此二叉树:" << endl;
cout << "前序:" << endl;
preOrderPrint(T);
cout << endl;
cout << "中序:" << endl;
inOrderPrint(T);
cout << endl;
cout << "后序:" << endl;
postOrderPrint(T);
cout << endl;

return 0;
}

// 第一个参数务必要声明为指针的引用类型
BinaryTreeNode * construct( BinaryTreeNode * &T, int *pre, int *in, int len )
{
// 如果输入的前序和中序序列不匹配
if (TAG == 1) return NULL;

// 如果为空树
if (len == 0) {
return NULL;
}

// 构造新结点
T = new BinaryTreeNode();
T->value = pre[0];

// 找到中序遍历数组中左右子树的" 分割点 "
int i=0;
while (in[i] != pre[0]) {
i++;
}

// 如果输入的前序和中序序列不匹配
if (i >= len) {
TAG = 1;
return NULL;
}

// a, b为左,右子树的前序遍历在pre的起始位置
int a,b;
// p, q为左,右子树的中序遍历在in中的起始位置
int p,q;
p = 0;
q = i+1;
a = 1;
b = q;

// 递归构建左子树
T->left = construct(T->left, &pre[a], &in[p], i);
// 递归构建右子树
T->right = construct(T->right, &pre, &in[q], len-i-1);

return T;
}

/*
* 三种测试用的遍历函数
*/
// 先序
void preOrderPrint( BinaryTreeNode *T ) {
if (!T) return;
cout << T->value << " ";
preOrderPrint(T->left);
preOrderPrint(T->right);
}

// 中序
void inOrderPrint( BinaryTreeNode *T ) {
if (!T) return;
inOrderPrint(T->left);
cout << T->value << " ";
inOrderPrint(T->right);
}

// 后序
void postOrderPrint( BinaryTreeNode *T ) {
if (!T) return;
postOrderPrint(T->left);
postOrderPrint(T->right);
cout << T->value << " ";
}


[b]运行测试


  正常输入:

  


  异常输入( 结点数为0或负数 ):

  


  异常输入( 前序,中序序列不匹配 ):

  


小结

  1. 对于一些较为复杂的问题,需要通过画图来细致分析,从而发现规律。

  2. 分析问题要全面,应当在考虑到一般输入后,思考特殊输入的问题。

  3. 递归思想可以解决很多经典的问题。

  4. 留意函数参数的传递方式是值传递,尤其是在传递的参数为指针时。必要时采用引用可以避免繁琐的多级指针。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: