已知前、中、后序中两种遍历结果以重建二叉树
当知道前序遍历、中序遍历、后序遍历中的两种遍历方式,就能够反推出二叉树的结构了。
但是要注意一点:已知前序遍历&后序遍历结果时,二叉树不能有节点的度为1!!!
提醒: 此处默认二叉树节点存储的值为int类型,其他类型,如char等可以将vector换为string等
例题:遍历问题,美国血统
一、已知前&中序遍历结果
// 对应洛谷:https://www.luogu.com.cn/problem/P1827
#include <iostream>
#include <unordered_map>
#include <vector>
using namespace std;struct Node
{int val;Node *l, *r;Node(int x) : val(x), l(nullptr), r(nullptr) {}
};class BinaryTreeBuilder
{
private:unordered_map<int, int> inorderMap; // 存储中序遍历中各元素的索引位置int preorderIndex; // 前序遍历的当前索引public:// 主函数,依据中序与前序遍历构建二叉树Node* buildTree(vector<int>& preorder, vector<int>& inorder){for (int i = 0; i < (int)inorder.size(); i++) inorderMap[inorder[i]] = i;preorderIndex = 0;return buildHelper(preorder, 0, inorder.size()-1);}private:// 递归辅助函数Node* buildHelper(vector<int>& preorder, int inorderStart, int inorderEnd){if (inorderStart > inorderEnd) return NULL;// 前序遍历的当前元素即为根节点int rootVal = preorder[preorderIndex++];Node *root = new Node(rootVal);// 在中序遍历中找到根节点的位置int rootIdx = inorderMap[rootVal];// 构建左、右子树(递归)root->l = buildHelper(preorder, inorderStart, rootIdx-1); // 中序遍历中在根节点前的都属于左子树root->r = buildHelper(preorder, rootIdx+1, inorderEnd); // 在后面的是右子树部分return root;}public:// 销毁树void destroyTree(Node *root){if (root == NULL) return;destroyTree(root->l);destroyTree(root->r);delete root;}// 中序遍历验证(左-根-右)void inOrder(Node *root){if (root == NULL) return;inOrder(root->l);cout << root->val << " ";inOrder(root->r);}// 前序遍历验证(根-左-右)void preOrder(Node *root){if (root == NULL) return;cout << root->val << " ";preOrder(root->l);preOrder(root->r);}// 后序遍历(左-右-根)void postOrder(Node *root){if (root == NULL) return;postOrder(root->l);postOrder(root->r);cout << root->val << " ";}
};int main()
{// 示例:根据给定的中序和前序遍历构建二叉树vector<int> preorder = {3, 9, 20, 15, 7}; // 前序遍历结果vector<int> inorder = {9, 3, 15, 20, 7}; // 中序遍历结果BinaryTreeBuilder builder;Node* root = builder.buildTree(preorder, inorder);cout << "构建的二叉树遍历结果:" << endl;cout << "前序遍历: ";builder.preOrder(root);cout << endl;cout << "中序遍历: ";builder.inOrder(root);cout << endl;cout << "后序遍历: ";builder.postOrder(root);cout << endl;builder.destroyTree(root);root = NULL;cout << "二叉树销毁完毕!" << endl;return 0;
}
二、已知前&后序遍历结果
注意:二叉树不能有节点的度为1!!!
遍历问题这道题是已知前、后序遍历求有多少种中序遍历结果,中序遍历中度等于1的节点是影响结果的因素,这个也是为什么在已知前、中、后序中两种遍历结果以重建二叉树中设置度不等于1。看了题解知道了一个知识点:前序中出现AB,后序中出现BA,则这个节点只有一个儿子。而这样的话只要找个度为1的节点数n,那么结果就是pow(2, n+1)。但是我们答题的时候一般使用找AB, BA的方法,使用substr(i, 2)截取AB,reverse得到BA,再find。
// 后序遍历的最后一个元素是根节点
// 但是这要求不能有度为1的节点(这不就是哈夫曼树嘛)
/*
解决方法:
1. 前序遍历的第一个元素是树的根节点
2. 后序遍历的最后一个元素也是树的根节点
3. 前序遍历中根节点之后的第一个元素通常是左子树的根节点(如果存在左子树)
4. 在后序遍历中找到这个左子树根节点的位置,就可以划分左右子树的范围
*/
#include <iostream>
#include <unordered_map>
#include <vector>
using namespace std;struct Node
{int val;Node *l, *r;Node(int x) : val(x), l(nullptr), r(nullptr) {}
};class BuildTree
{
private:unordered_map<int, int> postorderMap;int preorderIndex;public:Node *build(vector<int> preorder, vector<int> postorder){for (int i = 0; i < (int)postorder.size(); i++) postorderMap[postorder[i]] = i;preorderIndex = 0;return buildHelper(preorder, 0, postorder.size()-1);}private:Node *buildHelper(vector<int> preorder, int postStart, int postEnd){if (postStart > postEnd || preorderIndex >= (int)preorder.size()) return NULL;int rootVal = preorder[preorderIndex++];Node *root = new Node(rootVal);if (postStart == postEnd) return root; // 只有一个节点,则直接返回// 在后序遍历中找到左子树根节点的位置int lRootVal = preorder[preorderIndex];int lRootIdx = postorderMap[lRootVal];root->l = buildHelper(preorder, postStart, lRootIdx);root->r = buildHelper(preorder, lRootIdx+1, postEnd-1);return root;}public:// 销毁树void destroyTree(Node *root){if (root == NULL) return;destroyTree(root->l);destroyTree(root->r);delete root;}// 中序遍历验证(左-根-右)void inOrder(Node *root){if (root == NULL) return;inOrder(root->l);cout << root->val << " ";inOrder(root->r);}// 前序遍历验证(根-左-右)void preOrder(Node *root){if (root == NULL) return;cout << root->val << " ";preOrder(root->l);preOrder(root->r);}// 后序遍历(左-右-根)void postOrder(Node *root){if (root == NULL) return;postOrder(root->l);postOrder(root->r);cout << root->val << " ";}
};int main()
{// 示例:根据给定的前序和后序遍历构建二叉树vector<int> preorder = {1, 2, 4, 5, 3, 6, 7}; // 前序遍历结果vector<int> postorder = {4, 5, 2, 6, 7, 3, 1}; // 后序遍历结果BuildTree builder;Node* root = builder.build(preorder, postorder);cout << "根据前序和后序遍历重建的二叉树:" << endl;cout << "前序遍历: ";builder.preOrder(root);cout << endl;cout << "中序遍历: ";builder.inOrder(root);cout << endl;cout << "后序遍历: ";builder.postOrder(root);cout << endl;// 释放内存builder.destroyTree(root);root = nullptr;cout << "二叉树内存已释放!" << endl;return 0;
}
三、已知中&后序遍历结果
// 这个和已知前&中差不多,因为只要找到根节点——后序遍历的最后一个元素,再将中序遍历中左右子树(根节点的前后)递归创建拼接即可
#include <iostream>
#include <unordered_map>
#include <vector>
using namespace std;struct Node
{int val;Node *l, *r;Node(int x) : val(x), l(nullptr), r(nullptr) {}
};class Tree
{
private:unordered_map<int, int> inorderMap;int postorderIndex;public:Node *build(vector<int> inorder, vector<int> postorder){for (int i = 0; i < (int)inorder.size(); i++) inorderMap[inorder[i]] = i;postorderIndex = postorder.size() - 1;return buildHelper(postorder, 0, inorder.size()-1);}private:Node *buildHelper(vector<int> postorder, int inorderStart, int inorderEnd){if (inorderStart > inorderEnd) return NULL;int rootVal = postorder[postorderIndex--];Node *root = new Node(rootVal);int rootIdx = inorderMap[rootVal];root->r = buildHelper(postorder, rootIdx+1, inorderEnd);root->l = buildHelper(postorder, inorderStart, rootIdx-1);return root;}public:// 销毁树void destroyTree(Node *root){if (root == NULL) return;destroyTree(root->l);destroyTree(root->r);delete root;}// 中序遍历验证(左-根-右)void inOrder(Node *root){if (root == NULL) return;inOrder(root->l);cout << root->val << " ";inOrder(root->r);}// 前序遍历验证(根-左-右)void preOrder(Node *root){if (root == NULL) return;cout << root->val << " ";preOrder(root->l);preOrder(root->r);}// 后序遍历(左-右-根)void postOrder(Node *root){if (root == NULL) return;postOrder(root->l);postOrder(root->r);cout << root->val << " ";}
};int main()
{// 示例:根据给定的中序和前序遍历构建二叉树vector<int> preorder = {9, 3, 15, 20, 7}; // 中序遍历结果vector<int> inorder = {9, 15, 7, 20, 3}; // 后序遍历结果Tree builder;Node* root = builder.build(preorder, inorder);cout << "构建的二叉树遍历结果:" << endl;cout << "前序遍历: ";builder.preOrder(root);cout << endl;cout << "中序遍历: ";builder.inOrder(root);cout << endl;cout << "后序遍历: ";builder.postOrder(root);cout << endl;builder.destroyTree(root);root = NULL;cout << "二叉树销毁完毕!" << endl;return 0;
}
