# 二叉树
特性
二叉树是指满足以下要求的树:
它可以没有根结点,作为一棵空树存在
如果它不是空树,那么必须由根结点、左子树和右子树组成,且左右子树都是二叉树
二叉树不能被简单定义为每个结点的度都是2的树。普通的树并不会区分左子树和右子树,但在二叉树中,左右子树的位置是严格约定、不能交换的
# 满二叉树
叶子节点全都在最底层,除了叶子节点之外,每个节点都有左右两个子节点。上文中的图就是满二叉树。
# 完全二叉树
- 特性
从第一层到倒数第二层,每一层都是满的,也就是说每一层的结点数都达到了当前层所能达到的最大值
最后一层的结点是从左到右连续排列的,不存在跳跃排列的情况(也就是说这一层的所有结点都集中排列在最左
堆其实就是一种完全二叉树,一般采用堆存储方式是数组
# 二叉搜索树
- 特性
- 是一棵空树
- 是一棵由根结点、左子树、右子树组成的树,同时左子树和右子树都是二叉搜索树,且左子树上所有结点的数据域都小于等于根结点的数据域,右子树上所有结点的数据域都大于等于根结点的数据域
满足以上两个条件之一的二叉树,就是二叉搜索树。
BST-> 中序遍历是递增的
# 平衡二叉树(AVL 树)
- 特性
是任意结点的左右子树高度差绝对值都不大于1的二叉搜索树。
# Trie 树
Google、百度一类的搜索引擎强大的关键词提示功能的背后,最基本的原理就 Trie 树,通过空间换时间,利用字符串的公共前缀,降低查询的时间以提高效率。除此之外,还有很多应用,比如:IP 路由中使用了 Trie 树的最长前缀匹配算法,利用转发表选择路径以及 IDE 中的智能提示等。
# B+ 树
我们知道,将索引存储在内容中,查询速度是比存储在磁盘中快的。但是当数据量很大的情况下,索引也随之变大。内存是有限的,我们不得不将索引存储在磁盘中。那么,如何提升从磁盘中读取的效率就成了工程上的关键之一。 大部分关系型数据库的索引,比如 MySQL、Oracle,都是用 B+ 树来实现的。B+ 树比起红黑树更适合构建存储在磁盘中的索引。B+ 树是一个多叉树,在相同个数的数据构建索引时,其高度要低于红黑树。当借助索引查询数据的时,读取 B+ 树索引,需要更少的磁盘 IO 次数。 一个 m 阶的 B 树满足如下特征:
每个节点中子节点的个数 k 满足 m > k > m/2,根节点的子节点个数可以不超过 m/2通过双向链表将叶子节点串联在一起,方便按区间查找m 叉树只存储索引,并不真正存储数据一般情况,根节点被存储在内存中,其他节点存储在磁盘中
# 树的遍历(前、中、后、层次)
- 前序遍历(根->左->右)
对于二叉树中的任意一个节点,先打印该节点,然后是它的左子树,最后右子树
//递归法
var preorderTraversal =function(root,array=[]){
if(root){
//递归实现
array.push(root.val);
preorderTraversal(root.left,array)
preorderTraversal(root.right,array)
}
return array;
}
//迭代法
var preorderTraversal = function(root) {
// 用一个栈辅助完成迭代遍历
const stack = [];
// 结果数组
const res = [];
// 如果当前节点为空或者栈为空时结束循环
while(root || stack.length>0) {
while(root) {
// 因为是前序遍历,所以根节点先存放到结果数组
res.push(root.val);
// 为了在我们走到左子树的底部时,能够回到根节点继续遍历右子树,因此,先将根节点存入栈中
stack.push(root);
// 继续遍历左子树,直到遍历到叶子节点
root = root.left;
}
// 左子树遍历完了,我们需要遍历右子树了,首先我们先要从栈顶把最近的一个根节点弹出
root = stack.pop();
// 遍历弹出的这个根节点的右子树
root = root.right;
}
return res;
};
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
- 中序遍历(左->根->右)
对于二叉树中的任意一个节点,先打印它的左子树,然后是该节点,最后右子树
//递归实现
var inorderTraversal= function(root,array=[]){
if(root){
inorderTraversal(root.left,array)
array.push(root.val)
inorderTraversal(root.right, array);
}
return array
}
//迭代法
const inorderTraversal=function(root,array=[]){
//初始化数据
const res =[]
const stack=[]
while(root||stack.length){
while(root){
stack.push(root);
root=root.left
}
root= stack.pop();
res.push(root.val)
root=root.right
}
return res
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
- 后序遍历(左->右->根)
对于二叉树中的任意一个节点,先打印它的左子树,然后是右子树,最后该节点
var postorderTraversal = function (root, array = []) {
if (root) {
postorderTraversal(root.left, array);
postorderTraversal(root.right, array);
array.push(root.val);
}
return array;
};
//迭代法
const postorderTraversal=function(root){
const res =[]
const stack=[]
while(root||stack.length){
while(root){
stack.push(root)
res.unshift(root.val)
root=root.right
}
root= stack.pop()
root.left;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
- DFS(深度优先遍历)
一个分枝走到底
- BFS(广度优先遍历)
逐层遍历
# 高频题
# 反转二叉树(简单) (opens new window)
- 递归
- 从根节点开始,递归的对树进行遍历。
- 从叶子结点开始进行翻转。
- 左右子树都已经翻转后,交换两棵子树的位置即可完成全部的翻转。
const invertTree =function (root){
if(root === null){
return null
}
invertTree(root.left)
invertTree(root.right)
const temp=root.left
root.left= root.right
root.right =temp
return root
}
//时间复杂度: O(n)
//空间复杂度: O(n)
2
3
4
5
6
7
8
9
10
11
12
13
# 相同的树(简单) (opens new window)
- 深度优先搜索DFS
- 如果两棵树都为空,则它们相同返回true
- 如果两个二叉树中有一个为空,则它们不同返回false
- 如果两个二叉树都不为空,首先判断根节点是否相同,不同则返回false
- 如果两个二叉树的根节点相同,则分别递归判断其左右子树是否相同
const isSameTree =function(p,q){
if(p ===null && q===null) return true
if(p === null || q===null) return false
if(p.val !==q.val) return false
return isSameTree(p.left,q.left) && isSameTree(p.right,q.right)
}
//时间复杂度: O(min(m, n))
//空间复杂度: O(min(m, n))
2
3
4
5
6
7
8
9
# 对称二叉树(简单) (opens new window)
- 递归
先明确,所谓"对称",也就是两个树的根节点相同且
- 第一个树的左子树与第二个树的右子树镜像对称
- 第一个树的右子树与第二个树的左树镜像对称
const isSymmetric= function (root){
if(root ===null){
return true
}
return isEqual(root.left,root.right)
}
const isEqual = function (left,right)
{
//递归终止条件
if(left === null && right ===null) return true
if(left === null || right === null )return false
//比较左右子树的root 值以及左右子树 是否对称
return left.val === right.val && isEqual(left.left, right.right)
&& isEqual(left.right,right.left)
}
//时间复杂度: O(n)
//空间复杂度: O(n)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 二叉树的最大深度(简单) (opens new window)
- DFS 深度优先搜索
树的深度= 左右子树的最大深度+1
const maxDepth =function(root){
if(!root){
return 0
}else{
const left = maxDepth(root.left)
const right = maxDepth(root.right)
return Math.max(left,right)+1
}
}
//时间复杂度: O(n)
//最坏空间复杂度: O(height), height 表示二叉树的高度
2
3
4
5
6
7
8
9
10
11
12
13
- BFS 广度优先搜索
层序遍历时记录树的深度
const maxDepth = function(root) {
//层数
let depth = 0
if (root === null) {
return depth
}
const queue = [root]
while (queue.length) {
let len = queue.length
while (len--) {
const cur = queue.shift()
cur.left && queue.push(cur.left)
cur.right && queue.push(cur.right)
}
depth++
}
return depth
};
//时间复杂度: O(n)
//空间复杂度: O(n)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 二叉树的最小深度 (opens new window)
- 递归DFS
- root 为空时,高度为 0
- root 的左右子树都为空时,高度为 1
- 如果左子树或者右子树为空时,返回另一棵子树的高度
- 否则返回两棵子树的高度最小值
const minDepth =function (root){
if(root ===null) return 0
if(root.left ===null && root.right ===null){
return 1
}
if(root.left ===null){
return 1 + minDepth(root.right)
}
if(root.right === null){
return 1+minDepth(root.left)
}
return Math.min(minDepth(root.left),minDepth(root.right))+1
}
//时间复杂度:O(n)
//空间复杂度:O(logn)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 平衡二叉树(简单) (opens new window)
- 平衡二叉树定义:一个二叉树每个节点的左右两个子树的高度差的绝对值不超过1
- 如果遍历完成,还没有高度差超过1的左右子树,则符合条件
- 判断左右子树高度差,超过1则返回false
- 递归左右子树
- 封装获取子树高度函数getHeight
const isBalanced =function (root){
if(!root) return true
if(Math.abs(getHeight(root.left) - getHeight(root.right))>1){
return false
}
return isBalanced(root.left) && isBalanced(root.right)
function getHeight(root){
if(!root) return 0
return Math.max(getHeight(root.left),getHeight(root.right))+1
}
}
//时间复杂度:O(n)
//空间复杂度:O(n)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 有序数组转换为二叉搜索树(简单) (opens new window)
- 递归
- 二叉搜索树的中序遍历是升序的,本题中给出的数组刚好是升序的,相当于通过中序遍历恢复二叉搜索树
- 可以选择升序序列中的任意位置的元素作为根节点,该元素左边的升序序列构建左子树,右边的升序序列构建右子树
- 题目又要求高度平衡,所以我们需要选择升序序列的中间位置的元素作为根节点即可
const sortedArrayToBST =function (nums){
const buildTree = (arr,left,right)=>{
if(left > right){
return null
}
let mid = Math.floor(left+(right -left)/2)
let root = new TreeNode(arr[mid])
root.left = buildTree(arr,left,mid-1)
root.right = buildTree(arr,mid+1,right)
return root
}
return buildTree(nums,0,nums.length-1)
}
// 时间复杂度O(n)
// 空间复杂度O(n)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 路径总和(简单) (opens new window)
- 递归
- 处理边界,节点不存在时返回false
- 左右子树都不存在时代表是叶子节点,判断是否符合条件
- 递归左右子树时进行转换,看能否找到 targetSum -root.val 的路径
const hasPathSum =(root,targetSum){
if(root === null){
return false
}
if(root.left === null && root.right === null){
return root.val === targetSum
}
return hasPathSum(root.left,targetSum - root.val) || hasPathSum(root.right,targetSum - root.val)
}
//时间复杂度: O(n)
//空间复杂度: O(H),H 是树的高度
2
3
4
5
6
7
8
9
10
11
12
13
# 二叉树的直经(简单) (opens new window)
- 递归dfs
一棵二叉树的直径长度是任意两个结点路径长度中的最大值 这条路径可能穿过也可能不穿过根结点
- 两个公式
- 最长路径=左子树最长路径 +右子树最长路径 +1(根结点)
- 高度(最大深度) = 左右子树中的最大深度+1(根结点)
const diameterOfBinaryTree =function (root){
let ans =1
function depth(node){
if(node ===null) return 0
let L =depth(node.left)
let R =depth(node.right)
ans =Math.max(ans,L+R+1)
return Math.max(L,R)+1
}
depth(root)
return ans -1
}
// 时间复杂度: O(n)
// 空间复杂度: O(H),H 为二叉树的高度
2
3
4
5
6
7
8
9
10
11
12
13
14
# 合并二叉树(简单) (opens new window)
- DFS递归
在 root1 上直接修改,将两个树对应的节点相加后,赋值给 root1,然后递归执行两个左右子树。
const mergeTrees = function(root1, root2) {
return preOrder(root1,root2)
};
function preOrder(root1,root2){
if(!root1 &&!root2) return null
if(!root1) return root2
if(!root2) return root1
root1.val+=root2.val
root1.left=preOrder(root1.left,root2.left)
root1.right=preOrder(root1.right,root2.right)
return root1
}
//时间复杂度: O(min(m,n))
//空间复杂度: O(min(m,n))
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 二叉搜索树的第k个节点(简单) (opens new window)
- 因为是搜索二叉树 先中序遍历 先右再中再左;
- 过程中看count是否等于k 是的话 res = node.val;return
const kthLargest =function(root,k){
let count = 1;
let res ;
function dfs (node){
if(res)return
if(!node) return
dfs(node.right);
if(k===count++){
res= node.val;
return
}
dfs(node.left)
}
dfs(root)
return res
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# N叉树的前序遍历(简单) (opens new window)
var preorder = function(root,arr=[]) {
if(!root)return arr
arr.push(root.val)
root.children.forEach((val)=>{
preorder(val,arr)
})
return arr
};
2
3
4
5
6
7
8
# N叉树的后序遍历(简单) (opens new window)
DFS递归
const postorder = function (root)
{
if(root ===null){
return root
}
const res =[]
function dfs(root){
if(root ===null){
return
}
for(let i =0;i<root.children.length;i++){
dfs (root.children[i])
}
res.push(root.val)
}
dfs(root)
return res
}
//时间复杂度: O(n)
//空间复杂度: O(n)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 二叉树的层序遍历(中等) (opens new window)
- DFS 深度优先遍历
const levelOrder = function(root,depth=0,res=[]) {
if(!root)return res
if(!res[depth]) {
res[depth]=[]
}
res[depth].push(root.val)
levelOrder(root.left,depth+1,res)
levelOrder(root.right,depth+1,res)
return res
};
2
3
4
5
6
7
8
9
10
11
12
- BFS 广度优先遍历
根据层次 返回其对应的结果集合
- 边界处理,初始化队列 queue 和存放结果的数组 res。
- 外层循环遍历层级结构,内层循环遍历每一层的子节点。
- 遍历时需要记录当前层的遍历次数 len 以及当前层的节点数组 arr。
- 取得 node 依次出队,并依次存入当前层的节点数组中。
- 若存在左右子节点,则依次入队,并更新 len。
- 遍历完后返回结果 res。
const levelOrder =function(root){
if(!root) return []
const queue =[root]
const res =[]
while (queue.length>0){
const arr =[]
let len =queue.length
while(len){
let node =queue.shift()
arr.push(node.val)
if(node.left){
queue.push(node.left)
}
if(node.right){
queue.push(node.right)
}
len --
}
res.push(arr)
}
return res
}
//时间复杂度: O(n)
//空间复杂度: O(n)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 剑指Offer32. 从上到下打印二叉树II(简单) (opens new window)
- 解题思路
这道题其实就是我们二叉树的层序遍历,将每一层的节点依次输出。这里可以使用一个技巧,增加一个变量k代表当前遍历到二叉树的第几层,默认为0,即默认是二叉树的第一层
首先,先设计一下我们的递归函数
函数意义:将 root 为根节点端的左子树和右子树添加到当前层k所在的数组中
边界条件:root 为空时无需遍历,直接返回,直接返回结果数组
递归过程:分别递归遍历 root 的左子树和右子树,别忘了层数需要加1
function levelOrder(root: TreeNode | null,k=0, res: number[][] = []): number[][] {
// 边界条件,当root不存在时,直接返回结果数组
if(!root) return res;
// 如果结果数组的长度为k,说明我们准备遍历新的一层,但这层还没有数组来存放数据,所以要新建一个数组放到结果数组末尾
if(res.length===k) res.push([]);
// 将根节点的值放到第k位的数组中
res[k].push(root.val);
// 递归遍历左子树、右子树
levelOrder(root.left, k+1, res);
levelOrder(root.right, k+1, res);
return res;
};
2
3
4
5
6
7
8
9
10
11
12
13
# 二叉树的层序遍历Ⅱ (opens new window)
function _lavelOrder(root: TreeNode|null, k: number, arr: number[][] = []): number[][] {
if(!root) return arr;
if(arr.length===k) arr.push([]);
arr[k].push(root.val);
_lavelOrder(root.left, k+1, arr);
_lavelOrder(root.right, k+1, arr);
return arr;
}
function levelOrderBottom(root: TreeNode | null): number[][] {
const res = _lavelOrder(root, 0);
// 使用双指针方式交换反转数组
for(let i=0,j=res.length-1;i<j;i++,j--) {
[res[i], res[j]] = [res[j], res[i]];
}
return res;
// 也可以直接使用数组自带的反转方法
// return res.reverse();
};
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 二叉树的锯齿形层序遍历 (opens new window)
- 解题思路
这一题其实是上面两题思路的综合,我们只需要先按照正常的层序遍历输出得到结果数组,然后将结果数组中的奇数行进行翻转就可以了。
当然,我们可以用更好的方法,就是在进行层序遍历时,直接判断当前的层数k是奇数还是偶数,如果是奇数就往数组前面添加元素,否则往数组后面添加元素,这样就不需要额外再反转数组了。
function _zigzagLevelOrder(root: TreeNode | null, k:number=0, res: number[][]=[]): number[][] {
if(!root) return res;
if(k===res.length) res.push([]);
res[k].push(root.val);
_zigzagLevelOrder(root.left, k + 1, res);
_zigzagLevelOrder(root.right, k + 1, res);
return res;
};
function zigzagLevelOrder(root: TreeNode | null): number[][] {
const res = _zigzagLevelOrder(root);
// 将奇数行反转
return res.map((item, index) => {
if(index&1) {
item = item.reverse();
}
return item;
})
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 二叉树的光照节点(剑指 Offer II 046. 二叉树的右侧视图) (opens new window)
- 题目
// 从右侧有一束光照过来,请输出光能照到的所有节点。
// 例如:
// 给定二叉树: [3,9,20,null,null,15,7,16]
// 3
// / \
// 9 20
// / \
// 15 7
// /
// 16
// 输出:
// [3,20,7,16]
2
3
4
5
6
7
8
9
10
11
12
13
14
15
- dfs
思路:使用DFS递归遍历树的所有节点,创建结果数组res,将当前节点的层级当做res的下标、val当做res的值,以此更新res,右边节点的值会不断覆盖左边节点,得到的数组就是从右侧所能看到的节点值。
var rightSideView = function (root) {
const res = [];
function dfs(node, level) {
if (!node) return;
res[level] = node.val;
dfs(node.left, level + 1);
dfs(node.right, level + 1);
}
dfs(root, 0);
return res;
};
2
3
4
5
6
7
8
9
10
11
- 双队列 层序遍历
每层从右节点开始加入 那么每层的第一个·元素就是我们所看到的节点
function exposedElement(root){
if(!root){
return []
}
const queue =[root]
const res = []
while(queue.length){
let lastVal = null
for(let i =queue.length;i>0;i--){
const node = queue.shift()
lastVal =node.val;
if(node.left){
queue.unshift(node.left);
}
if(node.right){
queue.unshift(node.right)
}
}
if(lastVal){
res.push(lastVal)
}
}
return res
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 树的子结构(中等) (opens new window)
首先找到A树中和B树根节点相同的节点
从此节点开始,递归AB树比较是否有不同节点
function HasSubtree(pRoot1,pRoot2){
let result =false
if(pRoot1 && pRoot2){
if(pRoot1.val ===pRoot2.val){
result = compare(pRoot1,pRoot2)
}
if(!result){
result = HasSubtree(pRoot1.right,pRoot2);
}
if(!result){
result = HasSubtree(pRoot1.left,pRoot2);
}
}
return result
}
function compare(pRoot1,pRoot2){
if(pRoot2 ===null){
return true
}
if(pRoot1===null){
return false
}
if(pRoot1.val !== pRoot2.val){
return false
}
return compare(pRoot1.right,pRoot2.right) && compare(pRoot1.left, pRoot2.left);
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# 寻找重复的子树(中等) (opens new window)
- 后序遍历+序列化+hash存储
用递归,就必须知道以当前节点以它为根的这棵二叉树长什么样,还需要知道以其它节点为根的二叉树长什么样,才能进行比较
要解决以当前节点为根的二叉树长什么样,就得用序列化它的结构
要知道以当前节点为根的二叉树长什么样,用后序遍历最是佳的方式
要知道以其它节点为根的二叉树长什么样,就得要把序列化二叉树的结构的结果存储起来
const findDuplicateSubtree =function (root){
// 记录所有子树
const count = new Map();
const res =[]
const collect= (node)=>{
// 对于空节点,可以用一个特殊字符表示
if(!node){
return "#"
}
//将左右子树序列化成字符串,左右子树加上自己,就是以为自己为根的二叉树序列化结果
const key = node.val + "," +collect(node.left)+collect(node.right)
//让每个节点把自己的序列化结果存进去,对于每个节点,就可以知道有没有其他节点的子树和自己重复了
let freq = count.get(key) || 0;
if(freq ==1){
//有人和我重复,把自己加入结果列表
res.push(node)
}
count.set(key,freq+1);
return key;
}
collect(root)
return res
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# 验证二叉搜索树(中等) (opens new window)
- 中序遍历
二叉搜索树需要满足以下三条件:
- 节点的左子树只包含小于当前节点的数
- 节点的右子树只包含大于当前节点的数
- 所有左子树和右子树自身必须也是二叉搜索树
- 二叉搜索树在中序遍历得到序列一定是升序
- 进行中序遍历,判断当前节点是否大于前一个节点
- 如果比前一个大,说明满足,则继续遍历,否则直接返回 false
const isValidBST =function (root){
let prev = -Infinity
let result = true
function inorder(root){
if(root === null){
return
}
inorder(root.left)
if(root.val<=prev){
result =false
return
}
prev =root.val
inorder(root.right)
}
inorder(root)
return result
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 二叉树的最近公共祖先(中等) (opens new window)
- DFS递归
- 从根节点开始遍历,递归左右子树
- 递归终止条件:当前节点为空或者等于 p 或 q,返回当前节点
- p,q 可能在相同的子树中,也可能在不同的子树中
- 如果左右子树查到节点都不为空,则表示 p 和 q 分别在左右子树中,当前节点就是最近公共祖先
- 如果左右子树中有一个不为空,则返回为空节点
const lowestCommonAncestor =function (root,p,q){
if(root ===null || root === p || root ===q ) return root
let left = lowestCommonAncestor(root.left,p,q)
let right = lowestCommonAncestor(root.right,p,q)
if(left !==null && right !== null){
return root
}
return left !== null ?left:right
}
//时间复杂度: O(n)
//空间复杂度: O(n)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 重建二叉树(中等) (opens new window)
- 前序遍历的第一个元素一定是根节点,这里是3
- 找到根节点之后,根节点在中序遍历中把数组一分为二,即两个数组[9]和[15, 20, 7],这两个数组分别是根节点3的左子树和右子树的中序遍历。
- 前序遍历数组去掉根节点之后是[9,20,15,7],而这个数组的第1项[9](左子树的大小)和后3项[20, 15, 7](右子树的大小)又分别是左子树和右子树的前序遍历 到这里已经很明显了,用递归
function TreeNode(val){
this.val =val;
this.left =this.right =null;
}
const buildTee =function (preorder,inorder){
if(preorder.length){
let head =new TreeNode(preorder.shift())
let index =inorrder.indexOf(head.val);
head.left =buildTree(
preorder.slice(0,index)
inorder.slice(0,index)
);
head.right =buildTree(preorder.slice(index),inorder.slice(index+1));
return head;
}else{
return null
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 二叉树的序列化和反序列化(困难) (opens new window)
- DFS 递归
const serialize = function (root){
const res =[]
function dfs(node){
if(node ===null){
res.push(null)
return
}
res.push(node.val)
dfs(node.left)
dfs(node.right)
}
dfs(root)
return res
}
const deserialize =function (data){
function dfs(){
if(data.length === 0) return null
let val =data.shift()
if(val ===null) return null
let node = new TreeNode(val)
node.left =dfs()
node.right =dfs()
return node
}
return dfs()
}
//时间复杂度: O(n)
//空间复杂度: O(n)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31