思路:
先来一个例子:
先序遍历序列为:FDXEAG
中序遍历序列为:XDEFAG
要根据先序序列和中序序列确定这个二叉树,通用的步骤为:
1.根据先序序列的第一位确定这棵树的根;
2.在中序序列中找到根的所在的位置,根的左边就是该树的左子树的节点,根的右边就是该树的右子树的节点;
3.根据树的左子树节点和右子树节点在先序序列中分别找到对应的子串;
4.对3中找到的两个子串分别重复1 2 3步,左子树节点用于构建左子树,右子树节点用于构建右子树,直到所有的节点都用于构建这棵树。
示例:
根据以上的案例走一遍:
1.由先序遍历序列为:FDXEAG可知,树的根节点为F;
2.在中序遍历序列XDEFAG找到F的索引为3,左边XDE就是该树的左子树的节点,右边AG就是该树的右子树的节点;
3.根据树的左子树节点XDE和右子树节点AG在先序序列中分别找到对应的子串,分别为:DXE和AG;
4.对DXE和AG分别重复步骤1 2 3,DXE用于构建左子树,AG用于构建右子树,直到所有的节点都用于构建树。
每执行完第一轮步骤1 2 3,所用的根节点已经用于构建树了,后续就不用再考虑了。例如,执行完第一轮步骤1 2 3,根节点F已经用过了,后续就不用再考虑了。
分析:
相信思路都很明确,步骤大概也明白了,接下来代码实现中一个非常注重细节的地方就是递归构建左子树和右子树的部分,再详细说就是在递归调用构建树的函数中,我们要传入的两个参数应该怎么确定。
本次主要介绍利用先序遍历序列和中序遍历构建一个二叉树并输出后序遍历的方法,我们在递归调用构建树的函数中,我们要传入的两个参数当然就是子树的先序遍历序列和中序遍历序列。创建左子树时就传入左子树的先序遍历序列和中序遍历序列,创建右子树时就传入右子树的先序遍历序列和中序遍历序列。
以下根据以上案例详细分析:对于:
索 引:012345
先序遍历序列为:FDXEAG
中序遍历序列为:XDEFAG
将以上两个遍历序列分别命名为字符串s1,s2,即s1 = "FDXEAG", s2 = "XDEFAG"。根据s1可知树的根为F,在s2中寻找到F的索引(定义为pos)为3。根据s2可得左子树包括的字符有:XDE(由s2.substr(0, pos)获得),右子树包括的字符有:AG(由s2.substr(pos+1)获得),在s1中对应的字符串分别为:DXE(由s1.substr(1, pos)获得)和AG(由s1.substr(pos+1)获得)。根据以上获得的四个参数,可以递归创建左子树和右子树。
源代码:
//根据先序遍历和中序遍历确定一个二叉树
// 二叉树节点结构定义
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;
}
// 取先序遍历的第一个字符作为根节点
char c = str1[0];
// 在中序遍历中找到根节点的位置
int pos = str2.find(c);
// 创建根节点
TreeNode* root = new TreeNode(c);
递归构建左子树
root->leftChild = Build(str1.substr(1, pos), str2.substr(0, pos));
递归构建右子树
root->rightChild = Build(str1.substr(pos + 1), str2.substr(pos + 1));
return root;
}
// 后序遍历输出
void postOrder(TreeNode* root) {
if (root == NULL) {
return;
}
//先遍历左子树
postOrder(root->leftChild);
//再遍历右子树
postOrder(root->rightChild);
//输出当前根节点的值
cout << root->data;
return;
}
int main()
{
string s1, s2;
while (getline(cin, s1)) {
getline(cin, s2);
TreeNode* root = Build(s1, s2);
cout << "该树的后序遍历序列为:";
postOrder(root);
cout << endl;
}
return 0;
}
示例运行结果: