• leetcode每日一题(2020-06-18):1028. 从先序遍历还原二叉树


    题目描述:
    我们从二叉树的根节点 root 开始进行深度优先搜索。
    在遍历中的每个节点处,我们输出 D 条短划线(其中 D 是该节点的深度),然后输出该节点的值。(如果节点的深度为 D,则其直接子节点的深度为 D + 1。根节点的深度为 0)。
    如果节点只有一个子节点,那么保证该子节点为左子节点。
    给出遍历输出 S,还原树并返回其根节点 root。



    今日学习:
    1.复习map
    2.巩固树的相关知识


    先说思路:
    1.我本来看到输出是层序遍历的样子,就想着用hashmap来存,key存深度,value存节点数值
    2.我忽略了hashmap键值唯一的特性,存完之后发现不对,改用数组来存value
    3.我对树结构理解的相当不透彻,导致存完map我就蒙了不知道该怎么写了,因为我发现我这样存的话不同的树可能是同一个map
    4.看题解,发现根本和我的方法不一样,是用栈来做的,copy一番
    5.后来想想,hashmap中,如果下一层的数在前序遍历中是不挨着的,就说明左边有子树,如果是挨着的就说明左子树为null,然后又因为我自己存的value是数组,我又蒙了不知道怎么算了
    6.看到一个小伙伴用deeps[]和nodes[]两个数组来存前序遍历的节点以及深度,恍然大悟,我想要的不就是深度么,和是不是层序遍历关系不大,果断学习一波
    7.最后还看到可以用递归:1-2--3--4-5--6--7可以找到短线数目相同的,分成2--3--4和5--6--7,继续分成3和4、6和7...


    题解1:官方栈解法

    const recoverFromPreorder = (S) => {
      const stack = []
      for (let i = 0; i < S.length;) {
        let curLevel = 0 //一个curNode对应一个curLevel
        while (i < S.length && S[i] == '-') { // 避免循环半途中出界
          i++
          curLevel++    // 连字符个数代表level
        }
        const start = i // 记录当前节点值的开始位置
        while (i < S.length && S[i] != '-') {
          i++           // 指针移到当前节点值的结束位置
        }
        const curNode = new TreeNode(S.slice(start, i)) //创建当前节点
        if (stack.length == 0) { // ROOT入栈,不用找父亲,continue
          stack.push(curNode)
          continue
        }
        while (stack.length > curLevel) { // 栈顶不是父亲,栈顶出栈
          stack.pop()                     // 直到栈顶是父亲
        }
        if (stack[stack.length - 1].left) {       // 左儿子已存在
          stack[stack.length - 1].right = curNode // 安排为右儿子
        } else {
          stack[stack.length - 1].left = curNode  // 安排为左儿子
        }
        stack.push(curNode) // 节点肯定要入栈一次
      }
      return stack[0]       // 栈底就是根节点
    };
    

    题解2:双数组解法

    var recoverFromPreorder = function(S) {
        //用来存层序遍历的节点数组
        let nodes = []
        //用来存节点的树结构
        let node
        //用来存层序遍历的节点深度
        let deeps = []
        //用来计算'-'的个数,即节点深度
        let deep = 0
        //用来存节点数字
        let s = ''
        for(let i = 0; i < S.length; i++) {
            if(S[i] != '-') {
                //是数字,存起来
                s += S[i]
                //判断下一位是不是数字,是就继续存,不是就生成节点树结构
                if(S[i + 1] == '-' || i == S.length - 1) {
                    node = new TreeNode(s)
                    deeps.push(deep)
                    nodes.push(node)
                    //准备存下一个节点
                    deep = 0
                    s = ''
                }
            }else {
                //不是数字,深度+1
                deep++
            }
        }
        for(let i = 1; i < nodes.length; i++) {
            if(deeps[i] == deeps[i - 1]) {
                //和前一个节点深度相同,则表示它两是兄弟,找到父亲认亲为右儿子(左儿子已经是i - 1了)
                nodes[i - 2].right = nodes[i]
            }else if(deeps[i] > deeps[i - 1]) {
                //大于前一个节点深度,则表示它是前一个节点的左儿子
                nodes[i - 1].left = nodes[i]
            }else {
                //小于前一个深度,则表示它是前一个节点的叔父,需要找到它们的爷爷
                let temp = i
                while(deeps[i] <= deeps[temp]) {
                    temp--
                }
                //找到爷爷之后,将叔父认亲为右儿子
                nodes[temp].right = nodes[i]
            }
        }
        return nodes[0]
    }
    
  • 相关阅读:
    UML/ROSE学习笔记系列二:UML的概念模型
    UML/ROSE学习笔记系列五:用况对过程的描述
    UML/ROSE学习笔记系列四:图
    SQLSERVER2005 的作业调度(非原创)
    ORACLE 和SQLSERVER 两表之间批量更新数据对比
    彻底领悟javascript中的exec与match方法(非原创)
    对象,对象集合的简单Xml序列化与反序列化(非原创)
    JIT是库存管理的发展趋势
    UML/ROSE学习笔记系列一:建模原理、概念
    C# 多线程与异步操作实现的探讨(非原创)
  • 原文地址:https://www.cnblogs.com/autumn-starrysky/p/13157027.html
Copyright © 2020-2023  润新知