重建二叉树
题目:
输入一棵二叉树前序遍历和中序遍历的结果,请重建该二叉树
二叉树中每个节点的值都互不相同;
输入的前序遍历和中序遍历一定合法;
题目分析:
前序遍历是先访问根节点,再访问左子树,最后访问右子树
中序遍历是先访问左子树,再访问根节点,最后访问右子树
我们首先取出前序遍历序列的第一个元素,在中序遍历序列中找到这一个元素,此时在中序遍历序列中该元素的左侧为该元素的左子树,右侧为该元素的右子树
假设左子树的长度为k,则在前序遍历中该元素的后k个元素为该元素的左子素
同理,假设右子树的长度为m,则在前序遍历中左子树末尾元素的的后m个元素为该元素的右子树
解决方案:
根据这一特性,我们使用递归,首先找出子树根结点的位置
然后,我们继续遍历左右子树,左右子树的在前序遍历序列和中序遍历序列下标边界需要特别注意
如何确定左右子树的下标边界?
假设此时子树的前序遍历序列和中序遍历序列的边界为[pl, pr], [il, ir],根结点的下标为k
该子树的左子树
前序遍历序列的左边界(pl') = 左边界(pl) + 1
前序遍历序列的右边界下标(pr’) = 左边界的下标(pl + 1) + 左子树长度(length) - 1
左子树长度(length) = 根结点的下标位置(k) - 中序遍历的左边界(ir)
左子树中序遍历的左边界(il') = 中序遍历的左边界的下标(il)
左子树中序遍历的右边界(ir') = 根结点下标(k) - 1
子树的右子树
前序遍历序列的左边界(pl') = 前序遍历序列的右边界下标(pr' ) + 1
前序遍历序列的右边界(pr') = 前序遍历序列的右边界(pr)
右子树中序遍历的左边界(il') = 根结点的下标位置(k) + 1
右子树中序遍历的右边界(ir') = 右子树中序遍历的右边界(ir)
以下树为例:
第一次递归操作:[pl, pr, il, ir] = [0, 7, 0, 7]
前序遍历序列
中序遍历序列
第二次递归操作:[pl, pr, il, ir] = [1, 3, 0, 2]
前序遍历序列
中序遍历序列
代码:
1 /** 2 * Definition for a binary tree node. 3 * struct TreeNode { 4 * int val; 5 * TreeNode *left; 6 * TreeNode *right; 7 * TreeNode(int x) : val(x), left(NULL), right(NULL) {} 8 * }; 9 */ 10 class Solution { 11 public: 12 vector<int> preorder; 13 vector<int> inorder; 14 map<int, int> hash; 15 TreeNode* buildTree(vector<int>& _preorder, vector<int>& _inorder) { 16 preorder = _preorder; 17 inorder = _inorder; 18 for(int i = 0; i < inorder.size(); i++) hash[inorder[i]] = i; 19 return dfs(0, preorder.size()-1, 0, inorder.size()-1); 20 } 21 TreeNode* dfs(int pl, int pr, int il, int ir) 22 { 23 if(pl > pr) return nullptr; 24 int k = hash[preorder[pl]]; 25 auto root = new TreeNode(preorder[pl]); 26 auto left = dfs(pl + 1, pl + 1 + k -il -1, il, k - 1); //前序遍历左边界+1,左边界+左子树长度,中序遍历左边界,根元素位置-1 27 auto right = dfs(pl + k - il +1, pr, k + 1, ir); //左子树+1,前序遍历右边界,根元素位置+1,中序遍历右边界 28 root->left = left; 29 root->right = right; 30 return root; 31 } 32 };