注意:根据先序遍历与后序遍历只有在满二叉树的情况下才能确定一个唯一的树。这里介绍的是根据先序遍历+后序遍历构建一个满二叉树并输出中序遍历顺序。
思路:
先来一个例子:
先序遍历序列为:FDXEABG
后序遍历序列为:XEDBGAF
要根据先序序列和后序序列确定这个满二叉树,通用的步骤为:
1.根据先序序列的第一位或后序序列的最后一位确定这棵树的根root,但是不能用于确定它的左子树与右子树;
2.根据后序序列的倒数第二位确定右子树的根,这里命名为rRoot,这是用于定位的,用于确定根root的左子树与右子树;
3.在先序序列中找到右子树的根rRoot所在的位置,右子树的根rRoot的左边(除首位)就是该树的左子树的节点,右子树的根rRoot以及右边就是根root的右子树的节点;
4.根据左子树节点和右子树节点在后序序列中分别找到对应的子串;
5.对4中找到的两个子串分别重复1 2 3 4步,左子树节点用于构建左子树,右子树节点用于构建右子树,直到所有的节点都用于构建这棵树。
示例:
根据以上的案例走一遍:
1.由先序遍历序列为:FDXEABG可知,树root的根节点为F;
2.在后序遍历序列XEDBGAF中找到右子树的跟,这里命名为rRoot,也就是倒数第二个节点A
3.在先序遍历序列FDXEABG找到根root的右子树的跟A的索引为4,左边DXE(除首位)就是该树的左子树的节点,根A以及右边的ABG就是根root的右子树的节点;
4.根据树的左子树节点DXE和右子树节点ABG在后序序列中分别找到对应的子串,分别为:XED和BGA;
4.对XED和BGA(加上定位用的根节点A)分别重复步骤1 2 3,XED用于构建左子树,BGA用于构建右子树,直到所有的节点都用于构建树。
每执行完第一轮步骤1 2 3,所用的根节点已经用于构建树了,后续就不用再考虑了。例如,执行完第一轮步骤1 2 3,根节点F已经用过了,后续就不用再考虑了。
分析:
相信思路都很明确,步骤大概也明白了,接下来代码实现中一个非常注重细节的地方就是递归构建左子树和右子树的部分,再详细说就是在递归调用构建树的函数中,我们要传入的两个参数应该怎么确定。
本次主要介绍利用先序遍历序列和后序遍历序列构建一个二叉树并输出中序遍历的方法,我们在递归调用构建树的函数中,我们要传入的两个参数当然就是子树的先序遍历序列和后序遍历序列。创建左子树时就传入左子树的先序遍历序列和后序遍历序列,创建右子树时就传入右子树的先序遍历序列和后序遍历序列。
以下根据以上案例详细分析:对于:
索 引:0123456
先序遍历序列为:FDXEABG
中序遍历序列为:XEDBGAF
将以上两个遍历序列分别命名为字符串s1,s2,即s1 = "FDXEABG", s2 = "XEDBGAF"。
根据s1可知树的根root为F,根据s2可知树右子树的根rRoot为A,在s1中寻找到A的索引(定义为pos)为4。
根据s1可得左子树包括的字符有:DXE(由s1.substr(1, pos-1)获得),右子树包括的字符有:ABG(由s2.substr(pos)获得),
在s2中对应的字符串分别为:XED(由s2.substr(0, pos-1)获得)和BGA(由s2.substr(pos-1, str1.size() - pos)获得)。
根据以上获得的四个参数,可以递归创建左子树和右子树。
源代码:
//根据先序遍历和后序遍历确定一个二叉树,但必须是满二叉树
// 二叉树节点结构定义
struct TreeNode {
char data;
TreeNode* leftChild;
TreeNode* rightChild;
TreeNode(char c) : data(c), leftChild(NULL), rightChild(NULL) {}
};
// 根据先序遍历和后序遍历构建满二叉树
TreeNode* Build(string str1, string str2) {
if (str1.size() == 0) {
return NULL;
}
// 取先序遍历的第一个字符作为根节点
//根节点可以根据先序遍历的第一个字符和后序遍历的最后一个字符直接获得
TreeNode* root = new TreeNode(str1[0]);
//当先序遍历中只剩下一个节点时,已经在前面的创建子树的作为根节点使用过了,就不需要再做处理了
if (str1.size() > 1) {
// 在后序遍历中找到左子树根节点的位置
char c = str2[str2.size() - 2];
// 在先序遍历中找到根节点的位置
int pos = str1.find(c);
// 递归构建左子树
root->leftChild = Build(str1.substr(1, pos - 1), str2.substr(0, pos - 1));
// 递归构建右子树
root->rightChild = Build(str1.substr(pos), str2.substr(pos - 1, str1.size() - pos));
}
return root;
}
//中序遍历
void InOrder(TreeNode* root) {
if (root == NULL) {
return;
}
// 先遍历左子树
InOrder(root->leftChild);
//输出当前节点的值
cout << root->data;
// 再遍历右子树
InOrder(root->rightChild);
return;
}
int main()
{
string s1, s2;
while (getline(cin, s1)) {
getline(cin, s2);
// 根据先序遍历和后序遍历递归构建满二叉树
TreeNode* root = Build(s1, s2);
// 中序遍历该满二叉树
cout << "中序遍历结果为:";
InOrder(root);
cout << endl;
}
return 0;
}
示例运行结果: