根据二叉树的前序遍历和中序遍历的结果,重建这棵树。
分析:前序遍历的每一个节点,都是当前子树的根节点。同时,以对应的节点为边界,就会把中序遍历的结果分为左子树和右子树。
前序:a b d c e f
“a”是根节点
中序:d b a e c f
“a”是根节点,把字符串分成左右两个子树
"a"是前序遍历节点中的第一个元素,可以看出,它把中序遍历的结果分成"db"和'ecf"这两部分。
如果能够找到前序遍历中对应的左子树和右子树,就可以把"a"作为当前的根节点,然后依次递归下去,这样就能够依次恢复左子树和右子树的遍历结果。
上代码:
#include <iostream>
using namespace std;
#define TREELEN 6
struct NODE
{
NODE* pLeft;
NODE* pRight;
char chValue;
};
void rebuild(char* pPreOrder, char* pInOrder, int nTreeLen, NODE** pRoot)
{
//检查边界条件
if (pPreOrder == NULL || pInOrder == NULL)
{
return;
}
//获取前序遍历的第一个结点
NODE* pTemp = new NODE;
pTemp->chValue = *pPreOrder;
pTemp->pLeft = NULL;
pTemp->pRight = NULL;
//如果结点为空,把当前结点复制到根节点
if (*pRoot == NULL)
{
*pRoot = pTemp;
}
//如果当前长度为1,那么已经是最后一个节点
if (nTreeLen == 1)
{
return;
}
//寻找子树长度
char* pOrgInorder = pInOrder;
char* pLeftEnd = pInOrder;
int nTempLen = 0;
//寻找左子树的结尾
if (*pPreOrder != *pLeftEnd)
{
if (pPreOrder == NULL || pLeftEnd == NULL)
{
return;
}
nTempLen++;
//记录临时长度,以免溢出
if (nTempLen > nTreeLen)
{
exit(-1);
}
pLeftEnd++;
}
//寻找左子树长度
int pLeftLen = 0;
pLeftLen = (int)(pLeftEnd - pOrgInorder);
//寻找右子数长度
int pRightLen = 0;
pRightLen = (int)(nTreeLen - pLeftLen - 1);
//重建左子树
if (pLeftLen > 0)
{
rebuild(pPreOrder+1, pInOrder, pLeftLen, &((*pRoot)->pLeft));
}
//重建右子树
if (pRightLen > 0)
{
rebuild(pPreOrder+pLeftLen+1, pInOrder+pLeftLen+1, pRightLen, &((*pRoot)->pRight));
}
}
int main()
{
char szPreOrder[TREELEN] = {'a', 'b', 'd', 'c', 'e', 'f'};
char szInOrder[TREELEN] = {'d', 'b', 'a', 'e', 'c', 'f'};
NODE* pRoot = NULL;
rebuild(szPreOrder, szInOrder, TREELEN, &pRoot);
}