题目:由前序遍历和中序遍历重建二叉树
前序序列:1 2 3 4 5 6
中序序列:3 2 4 1 6 5
思路:每次以前序遍历序列的第一个数作为根结点,再在中序遍历序列里边找到根结点。根据中序遍历的特点我们可以知道,只有左子树全部访问完之后才会访问根结点,所以我们找到根结点之后,发现根结点之前的数都是根结点的左子树的所有结点,右边的数都是根结点右子树的所有结点。那么,可以以根结点为中心,将中序遍历序列分为两部分,左子树和右子树。
同时将与左子树等长的那部分前序序列作为新的前序序列,右子树也一样。将前序序列和中序序列每次都截成两部分,作为递归入口点进行递归。递归的出口就是找到叶子结点或者说新区间里边只剩下一个值时。
这道题再递归的时候,一定要注意边界条件的更新,如果是数组,要注意下标问题,vector则要弄明白end()的返回值。
#pragma once
template<class T>
struct BinaryTreeNode
{
T _data;
BinaryTreeNode<T>* _left;
BinaryTreeNode<T>* _right;
BinaryTreeNode(const T& d)
: _data(d)
, _left(NULL)
, _right(NULL)
{}
};
template<class T>
class ConstructTree
{
public:
typedef BinaryTreeNode<T> Node;
ConstructTree(T* preOrder, T* inOrder, size_t length)
{
if (preOrder == NULL || inOrder == NULL || length <= 0)
_root = NULL;
else
_root = _ConstructTree(preOrder, preOrder + length - 1,
inOrder, inOrder + length - 1);
}
protected:
Node* _ConstructTree(T* startPre, T* endPre, T* startIn, T* endIn)
{
//前序遍历序列的第一个数是根节点的值
Node* root = new Node(startPre[0]);
//递归结束条件,找到叶子结点,也就是区间缩小到只剩下一个值
if (startPre == endPre)
{
if (startIn == endIn && *startPre == *startIn)
return root;
else
throw exception("invalid input");
}
//在中序遍历中找到根结点
T* rootIn = startIn;
while (rootIn <= endIn && *rootIn != root->_data)
{
++rootIn;
}
//已经遍历完还没右找到根结点
if (rootIn == endIn && *rootIn != root->_data)
throw exception("invalid input");
//已经找到根节点
size_t leftLengthIn = rootIn - startIn;
//更新前序遍历的区间范围
T* newEndPro = startPre + leftLengthIn;
//构建左子树
if (leftLengthIn > 0)
{
root->_left = _ConstructTree(startPre + 1, newEndPro, startIn, rootIn - 1);
}
//构建右子树
if (leftLengthIn < endPre - startPre)
{
root->_right = _ConstructTree(newEndPro + 1, endPre, rootIn + 1, endIn);
}
return root;
}
protected:
Node* _root;
};
void Test()
{
int a[] = { 1, 2, 4, 7, 3, 5, 6, 8 };
int b[] = { 4, 7, 2, 1, 5, 3, 8, 6 };
ConstructTree<int> t1(a, b, 8);
}