给定两个整数数组 inorder 和 postorder ,其中 inorder 是二叉树的中序遍历, postorder 是同一棵树的后序遍历,请你构造并返回这颗 二叉树 。
示例 1:
输入:inorder = [9,3,15,20,7], postorder = [9,15,7,20,3]
输出:[3,9,20,null,null,15,7]
示例 2:
输入:inorder = [-1], postorder = [-1]
输出:[-1]
此题是根据中序遍历和后序遍历结果构建原二叉树。
首先回顾
我们分析inorder = [9,3,15,20,7], postorder = [9,15,7,20,3]
根据后序遍历结果可知3为根节点,然后查看中序遍历,可区分其左子树节点有9,其右子树节点有15,20,7。
后序遍历,如果该根节点有右子树,那么后序遍历序列根节点的前一个一定是根节点的右子树
算法步骤:
利用递归,来进行依次向下构建
递归需要传入参数 ToATree(TreeNode root, List
首先定义 left ,right用来表示该节点是否存在左右子树
root.val
获取root的index
index == 0
,即不存在左子树 index == in.size()
,即不存在右子树其次定义rootLeft,rootRight为null(代表root的左右子树)
如果存在右子树 , rootRight = new TreeNode(pos.get(pos.size()-2));
eg: 即根节点3存在右子树,其右子树为后序遍历root 的索引前一个节点
如果存在左子树,
rootLeft
接下来就是递归并切割集合
public static TreeNode buildTree(int[] inorder, int[] postorder) {
TreeNode root = new TreeNode(postorder[postorder.length-1]);
List<Integer> in=new ArrayList<>();
List<Integer> pos=new ArrayList<>();
for (int i = 0; i < inorder.length; i++) {
in.add(inorder[i] );
pos.add(postorder[i]);
}
ToATree(root,in,pos);
return root;
}
private static void ToATree(TreeNode root, List<Integer> in, List<Integer> pos) {
// 表示是否存在左右子树,初始化存在
int left = 0;
int right = 0;
// 获取 root 在 前序数组的指针
int rootInIndex = in.indexOf(root.val);
int rootPosIndex = pos.indexOf(root.val);
// 左子树
if(rootInIndex == 0) left=-1;
// 右子树
if(rootInIndex == in.size()-1) right=-1;
// 递归结束条件 该节点为叶子节点
if(left==-1&&right==-1) return;
TreeNode rootright = null;
TreeNode rootleft = null;
// 存在右子树
if(right == 0){
rootright = new TreeNode(pos.get(pos.size()-2));
}
// 存在左子树
// posIndex 右子树的第一个节点 也可能没有
int posIndex = 0;
if(left == 0){
// 如果存在右子树
if(right == 0){
int miniIndex = in.size();
for (int i = rootInIndex + 1; i < in.size(); i++) {
int index = pos.indexOf(in.get(i));
if (index<miniIndex){
miniIndex = index;
}
}
posIndex = miniIndex;
}
// 如果不存在右子树
else {
posIndex = pos.size() - 1;
}
rootleft = new TreeNode(pos.get(posIndex - 1));
}
if(rootleft!=null){
root.left = rootleft;
if(right==0){
ToATree(root.left,in.subList(0,rootInIndex),pos.subList(0,posIndex));
}
else {
ToATree(root.left,in.subList(0,rootInIndex),pos.subList(0,rootPosIndex));
}
}
if(rootright!=null){
root.right = rootright;
if(left==0){
ToATree(root.right,in.subList(rootInIndex+1,in.size()),pos.subList(posIndex,pos.size()-1));
}
else {
ToATree(root.right,in.subList(rootInIndex+1,in.size()),pos.subList(0,pos.size()-1));
}
}
}
但是这个效率不高啊,我觉得是使用集合的原因吗
自己查看了别人的思路,发现自己获取后序遍历时 左子树 和 右子树的临近点时过于复杂,其实可以直接使用长度相减即可。其次直接在数组上直接操作,不使用集合操作。
看来还是有继续优化的空间,但是奈何本人实力就这了。虽然效率不高,但是我觉得这个最容易理解。
首先递归参数ToATreeInt(root,inorder,postorder,inleft,inright,posleft,posright)
递归执行大概思路,为这个节点构建左右子树,然后继续对这个节点的左右子树构建子树。
所以结束条件是数组边界左边界等于右边界时 if(inleft == inright) return;
。
其次后面的原理与上面大致相同,不在赘述。
优化代码如下:
public static TreeNode buildTreeInt(int[] inorder, int[] postorder) {
TreeNode root = new TreeNode(postorder[postorder.length-1]);
int inleft = 0;
int inright = inorder.length -1;
int posleft = 0;
int posright = inorder.length - 1;
//
ToATreeInt(root,inorder,postorder,inleft,inright,posleft,posright);
return root;
}
private static void ToATreeInt(TreeNode root, int[] inorder, int[] postorder, int inleft, int inright, int posleft, int posright) {
// 递归结束条件
if(inleft == inright) return;
// left 1 存在左子树 0 不存在左子树
int left = 1;
int right = 1;
int inRootIndex = 0;
int posRootIndex = posright;
for( int i = inleft; i<=inright; i++){
if(inorder[i] == root.val){
inRootIndex = i;
break;
}
}
if (inRootIndex == inleft) left=0;
if (inRootIndex == inright) right=0;
TreeNode rootleft = null;
TreeNode rootright = null;
if(right == 1){
rootright = new TreeNode(postorder[posRootIndex-1]);
}
// posIndex定义为 root节点的左孩子在后序序列的下一个节点
// 当有右子树时,其下一个节点为后序序列的第一个root节点的右子树节点
// 没有右子树时,其下一个节点为root节点
int posIndex = 0;
if(left == 1) {
if(right == 1){
int rightLength =inright - inRootIndex;
posIndex = posRootIndex - rightLength;
}
else {
posIndex = posRootIndex;
}
rootleft =new TreeNode(postorder[posIndex - 1]);
}
if(rootleft!=null){
root.left = rootleft;
if(right == 1){
ToATreeInt(root.left,inorder,postorder,inleft,inRootIndex-1,posleft,posIndex-1);
}else {
ToATreeInt(root.left,inorder,postorder,inleft,inRootIndex-1,posleft,posright-1);
}
}
if(rootright!=null){
root.right=rootright;
if(left==1){
ToATreeInt(root.right,inorder,postorder,inRootIndex+1,inright,posIndex,posRootIndex-1);
}
else {
ToATreeInt(root.right,inorder,postorder,inRootIndex+1,inright,posleft,posRootIndex-1);
}
}
}