牛客网剑指offer第4题——重建二叉树
题目;
输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建二叉树并返回。
即根据二叉树的前序遍历结果和中序遍历结果,求解二叉树。
在这个问题之前,我们先来回顾一些基本概念;
(1)前序遍历
a、访问根节点;b、前序遍历左子树(注意这里说的并不是左子节点);c、前序遍历右子树。
(2)中序遍历
a、中序遍历左子树;b、访问根节点;c、中序遍历右子树。
(3)后序遍历
a、后序遍历左子树;b、后续遍历右子树;c、访问根节点。
在此,说明一个要点:前、中、后唯一的区别是对根节点的访问次序的差异。我们应该注意是的上面的比如:
a、访问根节点;b、前序遍历左子树;c、前序遍历右子树。
这句话表明了,二叉树的遍历,对其子树的遍历,遵循同样的方法。
思想如下:
a、根据前序遍历结果,第一个元素为二叉树的根结点;
b、观察中序遍历结果,根结点左侧的为左子树,若左子树根结点前(后)再无任何元素,则左(右)子树的左分支为空;根结点右侧的为右子树,若右子树根结点前(后)再无任何元素,则左(右)子树的左分支为空;
c、上面的过程是递归的。先找到当前树的根结点,然后划分为左右子树,再进入左子树重复上面的过程,最后进入右子树重复上面的过程,最终还原一棵树。
代码:
1 /** 2 * Definition for binary tree 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 TreeNode* reConstructBinaryTree(vector<int> pre,vector<int> vin) { 13 if(pre.size() ==0 || vin.size() == 0) 14 return nullptr; 15 //难点在于考虑需要定义哪些变量 16 TreeNode* root = new TreeNode(pre[0]);//要熟悉new的用法 17 //找到根节点在中序遍历中的位置,从而将树分为左子树和右子树结构。 18 int pos; 19 for( ; pos< vin.size();pos++) 20 if(vin[pos] == pre[0]) 21 break; 22 //重点是传入reConstructBinaryTree的参数是什么? 23 vector<int> son_vin_left,son_vin_right,son_pre_left,son_pre_right; 24 //考虑清楚为何要定义这四个变量,意义何在? 25 for(int i = 0;i <vin.size();i++) 26 { 27 if(i < pos)//重点是对于前序遍历 28 { 29 son_vin_left.push_back(vin[i]);//考虑会不会越界 30 son_pre_left.push_back(pre[i+1]); 31 } 32 if(i > pos) 33 { 34 son_vin_right.push_back(vin[i]);//考虑会不会越界 35 son_pre_right.push_back(pre[i]); 36 } 37 } 38 root->left = reConstructBinaryTree(son_pre_left ,son_vin_left); 39 root->right = reConstructBinaryTree(son_pre_right,son_vin_right); 40 return root; 41 42 } 43 };
对于上述代码的分析如下:
当左子树结构或者右子树为空,则返回。然后是,找到根节点在中序遍历的位置,于是中序遍历的左侧为左子树,右侧为右子数,对于他们的子树,也是采用同样的方式,因此采用递归的方式处理其左子树和右子树。
本题的重点是思考第23行的代码,其定义的四个变量,所代表的含义,为何要这四个变量,以及如何能够递归的定义他们。