排序二叉树的中序遍历序列是有序序列_美团面试题:根据前序序列和中序序列重建二叉树!...

cf6e3e12307dfafe6ed1bc1d43196ded.png

解题思路

助教:因为是树的结构,一般都是用递归来实现。

用数学归纳法的思想就是,假设最后一步,就是root的左右子树都已经重建好了,那么我只要考虑将root的左右子树安上去即可。

根据前序遍历的性质,第一个元素必然就是root,那么下面的工作就是如何确定root的左右子树的范围。

根据中序遍历的性质,root元素前面都是root的左子树,后面都是root的右子树。那么我们只要找到中序遍历中root的位置,就可以确定好左右子树的范围。

正如上面所说,只需要将确定的左右子树安到root上即可。递归要注意出口,假设最后只有一个元素了,那么就要返回。

一个实例

4d13ea766a7321d70f40c71013697e1d.png

前序序列是 3-9-20-15-7,中序序列是9-3-15-20-7,使用中序序列和前序序列重建二叉树。2d0506237b729ea71c541c3bfb144c09.png

3bf60592b96ca3dbc4798ffbbdc67bc3.png前序序列第一个是3,根据二叉树的性质,前序序列第一个节点就是代表的当前的根节点,所以3就是根节点。548e7d1c7f9187eddd59f806416d6b10.png从前序序列中获取到根节点3,然后在中序序列中3左边的是左子树的中序序列是9。5e4a9a8e4aa0618e71da28ba570af29f.png3是根节点,在中序序列中左侧是左子树中序序列9,根据左子树长度在前序序列中得出左子树的前序序列9(因为左子树的前序序列的长度和中序序列的长度肯定是相同的!) 然后现在我们获取到了左子树的前序序列和中序序列,而知道这些,我们就能构建出左子树是什么结构

ca3240109ece83d128a8f6a282cf280b.png292ddcbe446b693ca505120a505343e9.png

02b8bf6f4bd4b7bb8160c06c535b8982.png助教:现在我们递归进入到了左子树这一层,然后左子树的前序序列和中序序列一致,且序列长度都为1,所以左子树就是9这一个节点,左子树递归结束接下来开始右子树递归。这里可以知道递归的终止条件是:左子树的前序序列和中序序列一致,且序列长度都为1,也就是说明当前节点是叶子节点!所以递归到这里就可以找到出口了!

左子树结束了,接下来处理右子树,由于知道哪些是左子树的前序序列和中序序列了,所以剩下的就是右子树的前序序列和中序序列!如下图所示:e949aa79879698578495b1f14b817da9.png同理,知道右子树的前序序列和中序序列,我们就能构建出右子树这棵树,右子树的前序序列中第一个节点是右子树的根,也就是20。

0b4a0b3c83a9c26be4a6270b1eb1f14f.png同理,在右子树的中序序列中,20左侧的是当前树的左子树的中序序列15,根据当前树的左子树的长度为1,可以从前序序列得出当前树的左子树的前序序列是15.2b03320d2d10696984ae519049d57469.png

**由于前序序列和中序序列一致,且序列长度都为1,也就是说明当前节点是叶子节点!**所以20的左子树就是15!5b24c135a85f2561ee277b7fe42d477b.png抛掉左子树的部分就能得到20的右子树的前序序列和中序序列。如下图:2e50ac5a8d2054dd93b605c2655d9243.png同理,由于当前树的前序序列和中序序列都是7,所以20的右子树就是7.4c5d58f56e8c6ed09ed4be0e8c61b9db.png到这里,得到最终的二叉树结构!

动画展示

79bf6d7d169ac0827063d19eeffc3b6a.gif

当前代码实现

/**
 * Definition for binary tree
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
public class Solution {
    public TreeNode reConstructBinaryTree(int [] pre,int [] in) {
        if(pre.length == 0 || in.length == 0)
            return null;
        TreeNode root=reConstructBinaryTree(pre,0,pre.length-1,in,0,pre.length-1);
        return root;
    }
    private TreeNode reConstructBinaryTree(int[] pre,int startPreIndex,int endPreIndex,int[] in,int startInIndex,int endInIndex){
        if((startPreIndex == endPreIndex) && (startInIndex == endInIndex) && (pre[startPreIndex] == in[startInIndex]))
        {// 这里是进入到当前树的中序序列和后序序列一样的情况,且都序列长度为1,之间返回当前节点
            return new TreeNode(pre[startPreIndex]);
        }
        if(startInIndex > endInIndex || startPreIndex > endPreIndex ) //当当前树的左子树或者右子树为空
            return null;
        //int length = endInIndex - startInIndex;
        TreeNode root = new TreeNode(pre[startPreIndex]);
        int lengthLeftTree = 0;
        int lengthRightTree = 0;
        int flag = 0;
        int tempStartInIndex = -1;
        for( int i = startInIndex;i<= endInIndex;i++)
        {
            if(in[i] != root.val && flag == 0)
            {
                lengthLeftTree++; // 求左子树长度
            }else if(in[i] == root.val)
            {
                flag = 1;
                tempStartInIndex = i+1;//求根节点的索引
            }else if(flag == 1)
            {
                lengthRightTree++;//求右子树的长度
            }
        }
        root.left = reConstructBinaryTree(pre,startPreIndex+1,startPreIndex+lengthLeftTree,in,startInIndex,startInIndex+lengthLeftTree-1);
        // root.left是求出下一次递归中当前树的左子树的中序和前序序列然后再次回去进行构建左子树的二叉树
        root.right = reConstructBinaryTree(pre,startPreIndex+lengthLeftTree+1,endPreIndex,in,tempStartInIndex,endInIndex);
        return root;
    }
}

递归终止条件

        if(startInIndex > endInIndex || startPreIndex > endPreIndex ) //当当前树的左子树或者右子树为空
            return null;
661b5132149dc675c20016bdada8372f.png
4e9775fdd19ff508b8635d55c94207e0.png

假设树的左子树如果为空,如下面这样:0359ab44c8b6d037e0f991435c81bf85.png所以左子树的长度为0,当执行下面左子树递归的时候:

root.left = reConstructBinaryTree(pre,startPreIndex+1,startPreIndex+lengthLeftTree,in,startInIndex,startInIndex+lengthLeftTree-1);

由于lengthLeftTree为0,startPreIndex+1,startPreIndex+lengthLeftTree对应的reConstructBinaryTree参数是startPreIndex,endPreIndex,导致startPreIndex > endPreIndex,也就是导致一棵树的前序序列中开始索引大于结束索引,这明显是不成立的! 所以需要下面的递归终止条件:

        if(startInIndex > endInIndex || startPreIndex > endPreIndex ) //当当前树的左子树或者右子树为空
            return null;

当一棵树的左子树为空,导致startPreIndex > endPreIndex或者当一棵树的右子树为空,也就是startInIndex > endInIndex这种情况都是直接返回null,结束再往深递归。所以需要加一个判断左子树或者右子树为空的递归终止条件。

ed8a9a126fc6502fca2b551bdd3dbf1e.png小夕:因为如果到了叶子节点继续往下进行递归,由于叶子节点的左右子树都是空,所以也会触发递归终止条件,返回null,最后也就相当于又回到了叶子节点这里了!代码我这里写一下。

合并递归条件的五种语言实现代码

小夕实现的五种语言,实现的逻辑都是一样的,都是按照之前的讲解思路实现的,大家选取自己最熟悉的一种语言看就行。

Java

/**
 * Definition for binary tree
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
public class Solution {
    public TreeNode reConstructBinaryTree(int [] pre,int [] in) {
        if(pre.length == 0 || in.length == 0)
            return null;
        TreeNode root=reConstructBinaryTree(pre,0,pre.length-1,in,0,pre.length-1);
        return root;
    }
    private TreeNode reConstructBinaryTree(int[] pre,int startPreIndex,int endPreIndex,int[] in,int startInIndex,int endInIndex){

        if(startInIndex > endInIndex || startPreIndex > endPreIndex ) //当最初节点为空时候
            return null;
        //int length = endInIndex - startInIndex;
        TreeNode root = new TreeNode(pre[startPreIndex]); //TreeNode root = new TreeNode(pre[startInIndex]) 这里写成了中序遍历的起始点是错误的
        int lengthLeftTree = 0;
        int lengthRightTree = 0;
        int flag = 0;
        int tempStartInIndex = -1;
        for( int i = startInIndex;i<= endInIndex;i++)
        {
            if(in[i] != root.val && flag == 0)
            {
                lengthLeftTree++;
            }else if(in[i] == root.val)
            {
                flag = 1;
                tempStartInIndex = i+1;
            }else if(flag == 1)
            {
                lengthRightTree++;
            }
        }
        root.left = reConstructBinaryTree(pre,startPreIndex+1,startPreIndex+lengthLeftTree,in,startInIndex,startInIndex+lengthLeftTree-1);
        root.right = reConstructBinaryTree(pre,startPreIndex+lengthLeftTree+1,endPreIndex,in,tempStartInIndex,endInIndex);
        return root;
    }
}

php

<?php /*class TreeNode{
    var $val;
    var $left = NULL;
    var $right = NULL;
    function __construct($val){
        $this->val = $val;
    }
}*/function newReConstructBinaryTree($pre,$startPreIndex,$endPreIndex,$tin,$startInIndex,$endInIndex){if(count($pre) == 0 || count($tin) == 0 || $startInIndex > $endInIndex || $startPreIndex > $endPreIndex ) //这里用(startPreIndex-endPreIndex) != (startInIndex - endInIndex) 左右子树本来就不相等return null;//int length = endInIndex - startInIndex;
    $root = new TreeNode($pre[$startPreIndex]); //TreeNode root = new TreeNode(pre[startInIndex]) 这里写成了中序遍历的起始点是错误的
    $lengthLeftTree = 0;
    $lengthRightTree = 0;
    $flag = 0;
    $tempStartInIndex = -1;for($i = $startInIndex;$i<= $endInIndex;$i++)
    {if($tin[$i] != $root->val && $flag == 0)
        {
            $lengthLeftTree++;
        }else if($tin[$i] == $root->val)
        {
            $flag = 1;
            $tempStartInIndex = $i+1;
        }else if($flag == 1)
        {
            $lengthRightTree++;
        }
    }
    $root->left = newReConstructBinaryTree($pre,$startPreIndex+1,$startPreIndex+$lengthLeftTree,$tin,$startInIndex,$startInIndex+$lengthLeftTree-1);
    $root->right = newReConstructBinaryTree($pre,$startPreIndex+$lengthLeftTree+1,$endPreIndex,$tin,$tempStartInIndex,$endInIndex);return $root;
}function reConstructBinaryTree($pre, $vin){// write code herereturn newReConstructBinaryTree($pre,0,count($pre)-1,$vin,0,count($pre)-1);
}

JS

/* function TreeNode(x) {
    this.val = x;
    this.left = null;
    this.right = null;
} */
 
function newReConstructBinaryTree(pre,startPreIndex,endPreIndex,tin,startInIndex,endInIndex){
    if(pre.length == 0 || tin.length == 0 || startInIndex > endInIndex || startPreIndex > endPreIndex ) //这里用(startPreIndex-endPreIndex) != (startInIndex - endInIndex) 左右子树本来就不相等
        return null;
    //int length = endInIndex - startInIndex;
    var root = new TreeNode(pre[startPreIndex]); //TreeNode root = new TreeNode(pre[startInIndex]) 这里写成了中序遍历的起始点是错误的
    var lengthLeftTree = 0;
    var lengthRightTree = 0;
    var flag = 0;
    var tempStartInIndex = -1;
    for( var i = startInIndex;i<= endInIndex;i++)
    {
        if(tin[i] != root.val && flag == 0)
        {
            lengthLeftTree++;
        }else if(tin[i] == root.val)
        {
            flag = 1;
            tempStartInIndex = i+1;
        }else if(flag == 1)
        {
            lengthRightTree++;
        }
    }
    root.left = newReConstructBinaryTree(pre,startPreIndex+1,startPreIndex+lengthLeftTree,tin,startInIndex,startInIndex+lengthLeftTree-1);
    root.right = newReConstructBinaryTree(pre,startPreIndex+lengthLeftTree+1,endPreIndex,tin,tempStartInIndex,endInIndex);
    return root;
}
function reConstructBinaryTree(pre, vin){
    // write code here
    return newReConstructBinaryTree(pre,0,pre.length-1,vin,0,pre.length-1);
}

C++

/**
 * Definition for binary tree
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
 
class Solution {
public:
    TreeNode* reConstructBinaryTree(vector<int> pre,vector<int> vin) {
        int n = pre.size();
        int m = vin.size();
        if(n!=m || n == 0)
            return NULL;
        return construct(pre, vin, 0, n-1, 0, m-1);
    }
  
    TreeNode* construct(vector<int>& pre, vector<int>& vin, int l1, int r1, int l2, int r2){
        TreeNode* root = new TreeNode(pre[l1]);
        if(r1 == l1)
        {
            return root;
        }
        int val = pre[l1];
        int index;
        for(index = l2; index <= r2; index ++)
        {
            if(vin[index] == val)
                break;
        }
        int left_tree_len  = index - l2;
        int right_tree_len = r2 - index;
        if(left_tree_len > 0)
            root->left = construct(pre, vin, l1+1, l1+left_tree_len, l2, index-1);
        if(right_tree_len >0 )
            root->right = construct(pre, vin, l1+1+left_tree_len, r1, index+1, r2);
        return root;
    }
};

Python

# -*- coding:utf-8 -*-
class TreeNode:
     def __init__(self, x):
         self.val = x
         self.left = None
         self.right = None
class Solution:
    # 返回构造的TreeNode根节点
    def reConstructBinaryTree(self, pre, tin):
        # write code here
        if pre is None or tin is None or len(pre)==0 or len(tin)==0:
            return None
        return self.newReConstructBinaryTree(pre,0,len(pre)-1,tin,0,len(tin)-1)
    def newReConstructBinaryTree(self,pre,startPreIndex,endPreIndex,tin,startInIndex,endInIndex):
        if pre is None or tin is None or startInIndex > endInIndex or startPreIndex > endPreIndex:
            return None
        root = TreeNode(pre[startPreIndex])
        lengthLeftTree = 0;
        lengthRightTree = 0;
        flag = 0;
        tempStartInIndex = -1;
        i = startInIndex
        # for i in range(endInIndex):
        while(i <= endInIndex):
            if(tin[i] != root.val and flag == 0):
                lengthLeftTree += 1
            elif(tin[i] == root.val):
                flag = 1
                tempStartInIndex = i+1;
            elif(flag == 1):
                lengthRightTree += 1
            i += 1
        root.left = self.newReConstructBinaryTree(pre,startPreIndex+1,startPreIndex+lengthLeftTree,tin,startInIndex,startInIndex+lengthLeftTree-1);
        root.right = self.newReConstructBinaryTree(pre,startPreIndex+lengthLeftTree+1,endPreIndex,tin,tempStartInIndex,endInIndex);
        return root;

时间复杂度和空间复杂度

都是O(N),因为一直在使用最初的数组,时间复杂度是因为只遍历了一次树结构,所以是O(n)另外加一点小夕自己对于递归的理解。6c3e7d86bacca6e1ece9b21fc4725268.png

a747b6d59f9757940fc5b940c54a14af.png

小夕絮叨

本篇文章耗时良久,为了写得更好一些真的是绞尽小夕的脑汁了呀,一碰到递归的题目感觉做动画就比以往难了一些,不过小夕也还是咬咬牙坚持了下来,嘿嘿,如果大家觉得文章不错,不妨点赞(在看)、转发支持一下小夕!冲鸭!我们下周再见。原创能坚持下去真的很难,各位的在看和转发真的是我的唯一动力了,不要让小夕放弃呀~~~

076f3be6d835adea6a946f686eb8a47c.png         

973de3aa093222f383dbffa6bfc9dec2.png

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值