力扣每日一题打卡 - 构建二叉树专题
这道题是今天(2020-09-25)力扣官方的每日一题, 之前我写了题解,总结了 《构建二叉树专题》[1](可以阅读原文查看)。有一些朋友说我的复杂度有点高,实际上我只是为了新手容易理解才那么写的, 今天稍微修改一下放给大家看。
此题目和105. 从前序与中序遍历序列构造二叉树[2] 完全一致,如果你会其中一个,那么另外一个也一定会。
我们以题目给出的测试用例来讲解:
后序遍历是左右根
,因此postorder最后一个元素一定整个树的根。由于题目说明了没有重复元素,因此我们可以通过val去inorder找到根在inorder中的索引i。而由于中序遍历是左根右
,我们容易找到i左边的都是左子树,i右边都是右子树。
我使用红色表示根,蓝色表示左子树,绿色表示右子树。
根据此时的信息,我们能构造的树是这样的:
其中右子树由于个数大于1,我们无法确定,我们继续执行上述逻辑。我们postorder继续向前移动一位,这个时候我们得到了第二个根节点”20“,实际上就是右子树的根节点。
根据此时的信息,我们能构造的树是这样的:
我们不断执行上述逻辑即可。
代码:
class Solution:
def buildTree(self, inorder: List[int], postorder: List[int]) -> TreeNode:
# 实际上inorder 和 postorder一定是同时为空的,因此你无论判断哪个都行
if not inorder:
return None
root = TreeNode(postorder[-1])
i = inorder.index(root.val)
root.left = self.buildTree(inorder[:i], postorder[:i])
root.right = self.buildTree(inorder[i+1:], postorder[i:-1])
return root
简单起见,递归的时候每次我都开辟了新的数组,这个其实是没有必要的,我们可以通过四个变量来记录inorder和postorder的起始位置即可, 具体见下方代码区。
代码
class Solution:
def buildTree(self, inorder: List[int], postorder: List[int]) -> TreeNode:
def dfs(inorder, in_start, in_end, postorder, post_start, post_end):
if in_start > in_end: return None
if in_start == in_end: return TreeNode(inorder[in_start])
if post_start == post_end: return TreeNode(inorder[in_start])
root = TreeNode(postorder[post_end])
i = inorder.index(root.val)
root.left = dfs(inorder, in_start, i - 1, postorder, post_start, post_start + i - 1 - in_start)
root.right = dfs(inorder, i + 1, in_end, postorder, post_start + i - 1 - in_start + 1, post_end - 1)
return root
n = len(inorder)
return dfs(inorder, 0, n - 1, postorder, 0, n - 1)
「复杂度分析」
- 时间复杂度:由于每次递归我们的inorder和postorder的总数都会减1,因此我们要递归N次,故时间复杂度为
,其中N为节点个数。
- 空间复杂度:我们使用了递归,也就是借助了额外的栈空间来完成, 由于栈的深度为N,因此总的空间复杂度为
,其中N为节点个数。
关注公众号力扣加加,努力用清晰直白的语言还原解题思路,并且有大量图解,手把手教你识别套路,高效刷题。
Reference
[1]
构建二叉树专题: https://lucifer.ren/blog/2020/02/08/%E6%9E%84%E9%80%A0%E4%BA%8C%E5%8F%89%E6%A0%91%E4%B8%93%E9%A2%98/
[2]
105. 从前序与中序遍历序列构造二叉树: https://leetcode-cn.com/problems/construct-binary-tree-from-preorder-and-inorder-traversal/solution/si-lu-qing-xi-dai-ma-jian-ji-he-105ti-si-lu-yi-z-2/
如果觉得文章不错,帮忙点个在看呗
- 高性能网站架构方案(三) ——Varnish加速与Gearman任务分发
- mysql 性能优化方案 (转)
- 《Redis设计与实现》读书笔记(一)——简单动态字符串(SDS)
- Comet:基于 HTTP 长连接的“服务器推”技术
- 编码修炼 | 快速了解Scala技术栈
- VFS四大对象之三 struct dentry
- PHP 排序算法实现讲解
- PHP7新特性介绍
- VFS四大对象之四-struct file
- 《Redis设计与实现》读书笔记(二) ——Redis中的字典(Hash)
- 《Redis设计与实现》读书笔记(三) ——Redis中的链表
- 《Redis设计与实现》读书笔记(四) ——Redis中的跳跃表
- 解析Linux中的VFS文件系统之文件系统的注册(二)
- vivi虚拟摄像头驱动程序
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- Leetcode No.5 最长回文子串
- Python的循环、判断和各种表达式(长文系列第二篇)
- Java内存泄漏、性能优化、宕机死锁的N种姿势
- 从2.9秒到0.6秒,信息流首屏提效80%的秘诀
- 移植nodejs到嵌入式linux,让终端支持可使用js做些功能
- go语言版串口获取银商秘钥工具
- dotnet Microsoft.Recognizers.Text 超强大的自然语言关键词提取库
- Java 基础面试总结
- 银商TMS平台秘钥下载工具
- 小白向:Linux vim编辑器(一)
- 2020数据库面试题
- 银商TMS平台秘钥自动下载并形成文件工具
- top100习题
- 网易校招真题一
- java Hello world 源码执行流程详解