leetcode1028. 从先序遍历还原二叉树

原题地址: 从先序遍历还原二叉树

题目

我们从二叉树的根节点 root 开始进行深度优先搜索。

在遍历中的每个节点处,我们输出 D 条短划线(其中 D 是该节点的深度),然后输出该节点的值。(如果节点的深度为 D,则其直接子节点的深度为 D + 1。根节点的深度为 0)。

如果节点只有一个子节点,那么保证该子节点为左子节点。

给出遍历输出 S,还原树并返回其根节点 root。

示例

示例一:
在这里插入图片描述

输入:“1-2–3--4-5–6--7”
输出:[1,2,5,3,4,6,7]

示例二:
在这里插入图片描述

输入:“1-2–3—4-5–6—7”
输出:[1,2,5,3,null,6,null,4,null,7]

示例三:
在这里插入图片描述

输入:“1-401–349—90–88”
输出:[1,401,null,349,88,90]

解题思路

通过关观察字符串参数 traversal 可以发现:

  1. 如果后一个数字前的 '-' 的数量 > 前一个数字前的 '-' 的数量,后一个数字就是前一个数字的子节点。

  2. 如果后一个数字前的 '-' 的数量 < 前一个数字前的 '-' 的数量,那么两个数字不存在直接联系,需要返回上一级递归。

  3. 如果后一个数字前的 '-' 的数量 = 前一个数字前的 '-' 的数量,那么两个数字是兄弟关系,也需要返回上一级递归。

题目要求,如果节点只有一个子节点,也就是对应节点的数字(假设为n), n后面只存在一个数字(假设为n1),满足n1 前面 '-' 的数量 > n 前面'-'的数量,再其后的所有数字前 '-' 的数量都 <= n 前面 '-' 的数量。当遇到这种情况时需要保证该子节点为左子节点。那么在进行递归时应该考虑使用前序遍历,先创建当前节点,再创建左子节点,最后创建右子节点。

当确定了大概的框架时,再考虑具体为何实现,我的思路如下:

  1. 首先定义一个 index 成员变量用来扫描整个字符串 traversalindex 初始指向 0,当 index >= traversal.length() 时,说明整个树已经创建完毕,递归终止。
  2. 其次是需要在每一层递归中,纪录当前节点所在的深度,因此考虑定义另一个函数 buildTree(String traversal, int depth) ,参数为字符串 traversal 和当前深度 depthdepth 初始为 0
  3. 当进入该函数后,首先需要取得当前节点的数值并以此构建新的节点。因为数值可能是多位数,因此再定义一个函数 getValue(traversal) ,在函数中定义变量 begin = index,接着 index 开始向后扫描一直到 index 指向的字符 == '-' 时结束,那么此时 [begin,index) 区间内的字符串就是函数调用处需要创建的节点所对应的数值字符串,将这个字符串返回。
  4. 当创建新结点后,需要确定当前节点是否存在子节点,也就是需要继续向后扫描字符串并获取下一个节点的深度,那么此时可以再定义一个函数 getDepth(String traversal, int i) , i 初始等于 index, 函数内定义 depth 变量用于纪录深度,i 向后偏移当 i 指向的字符 == '-' 时,depth++,直到 i 指向的字符 != '-' 时结束并返回 depth
  5. 当获取到下一个节点的 depth 深度后,与当前节点的深度进行比较,如果 > 当前节点深度,则说明下一个节点是当前节点的子孩子,那么就先递归创建左子节点。在创建子节点时需要使用 index 获取节点值,因此 index 需要偏移到下一个节点所对应数值的首字符索引位置,而这个索引就等于当前的索引值加上下一个节点的深度。因此在递归前先将 index 进行偏移。
  6. 如果下一个节点的深度 <= 当前节点深度,则说明下一个节点不是当前节点的子节点,将当前节点返回到上一级。
  7. 回到上一级调用处时,当前节点的左子树已经构建完毕,需要构建当前节点的右子树,而在构建右子树时,同样需要确定下一个节点是否为当前节点的子节点,因此同样需要获取下一个节点的深度并进行比较。但是其实在之前左子树的递归函数中下一个节点的深度已经被计算过了,因此我这里将纪录下一个节点深度的变量定义为了成员变量,这样就避免了重复计算。
  8. 最终,会根据判断条件判断是否构建右子树。

代码

class Solution {
    int index;  
    int nextDepth;
    public TreeNode recoverFromPreorder(String traversal) {
        index = 0;
        return buildTree(traversal, 0);
    }

    private TreeNode buildTree(String traversal, int depth) {
        if(index >= traversal.length()) {
            return null;
        }

        String value = getValue(traversal);
        TreeNode root = new TreeNode(Integer.valueOf(value));
        nextDepth = getDepth(traversal, index);
        if(nextDepth > depth) {
           index = index + nextDepth;
           root.left = buildTree(traversal, nextDepth);
        } 
        if(nextDepth > depth) {
            index = index + nextDepth;
            root.right = buildTree(traversal, nextDepth);
        }  
        
        return root;
    }

    private String getValue(String traversal) {
        int begin = index;
        while (index < traversal.length() && traversal.charAt(index) != '-'){
            index++;
        }
        return traversal.substring(begin,index);
    }

    private int getDepth(String traversal, int i) {
         int depth = 0;
        if(i >= traversal.length()) {
            return depth;
        }
       
        while(traversal.charAt(i) == '-') {
            depth++;
            i++;
        }
        return depth;
    }
}

运行结果

在这里插入图片描述

  • 5
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值