// 节点不相等 返回false
if (level[i] !== null && level[l - i - 1] !== null) {
if (level[i].val !== level[l - i - 1].val) return false;
}
}
}
return true;
};
**复杂度分析**
* 时间复杂度:O(n)O(n)
* 空间复杂度:O(n)O(n)
当树是线性时,我们可能需要向队列中插入 O(n)O(n) 个节点
[]( )二叉树的最大深度
--------------------------------------------------------------------
给定一个二叉树,找出其最大深度。
二叉树的深度为根节点到最远叶子节点的最长路径上的节点数。
说明: 叶子节点是指没有子节点的节点。
**示例**
给定二叉树 \[3, 9, 20, null, null, 15, 7\],
3
/ \
9 20
/ \
15 7
返回它的最大深度 3。
### []( )方法一 递归查询排序
**思路**
遍历所有节点的深度,记录所有子节点的深度,然后筛选出最大的深度
**详解**
1. 创建一个空数组用来保存所有节点深度
2. 判断二叉树是否为空,空的直接返回 0,结束,非空二叉树继续
3. 遍历二叉树节点,没有左右子节点的,直接将当前 level 塞入之前定义的深度数组
4. 有左右子节点的就继续递归查询查询子节点的深度,传入的 level 也加 1 传递,直到二叉树遍历结束
5. 对深度数组排序返回最大值,也就是二叉树最大深度
**代码**
const maxDepth = function (root) {
// 创建保存节点深度的空数组
const levelList = [];
// 判断二叉树是否为空
if (root === null) {
return 0;
} else {
loop(root, 1);
return sort(levelList);
}
// 遍历二叉树子节点
function loop (node, level) {
// 没有子节点的,将当前深度保存进深度数组,有节点的继续遍历
if (node.left === null && node.right === null) {
levelList.push(level);
} else if (node.left !== null) {
loop(node.left, level + 1);
} if (node.right !== null) {
loop(node.right, level + 1);
}
}
// 对数组进行排序,返回最大深度
function sort (arr) {
arr.sort((a, b) => {
return b - a;
});
return arr[0];
}
};
**复杂度分析**
* 时间复杂度:O(n^2)O(n2)
* 空间复杂度:O(n)O(n)
### []( )方法二 递归
**思路**
递归二叉树的节点,获取左子树和右子树的最大深度,比较后,返回最大深度
**详解**
1. 判断二叉树是否为空,空的直接返回 0,结束,非空二叉树继续
2. 分别递归计算左右子树的最大深度
3. 根据返回两者的两者数字,比较后的返回二叉树的最大深度
**代码**
const maxDepth = function (root) {
if (root === null) {
return 0;
} else {
const leftDepth = maxDepth(root.left);
const rightDepth = maxDepth(root.right);
const childDepth = leftDepth > rightDepth ? leftDepth : rightDepth;
return 1 + childDepth;
}
};
**复杂度分析**
* 时间复杂度:O(n)O(n)
通过递归的方式查询了数的所有子节点。查询花费 O(n)O(n) 的时间。
* 空间复杂度:O(n)O(n)
每次递归都需要创建新的临时空间,空间复杂度 O(n)O(n)
[]( )二叉树的层次遍历、二叉树的序列化与反序列化和常数时间内插入删除、获得随机数
=================================================================================================
[]( )二叉树的层次遍历
--------------------------------------------------------------------
给定一个二叉树,返回其按层次遍历的节点值。(即逐层地,从左到右访问所有节点)。
**示例**
给定二叉树: [3,9,20,null,null,15,7]
3
/ \
9 20
/ \
15 7
返回其层次遍历结果:
[
[3],
[9,20],
[15,7]
]
### []( )方法一 递归
**思路**
最简单的解法就是递归,首先确认树非空,然后调用递归函数 helper(node, level),参数是当前节点和节点的层次。
**详解**
1. 输出列表称为 levels,当前最高层数就是列表的长度 len(levels)。比较访问节点所在的层次 level 和当前最高层次 len(levels) 的大小,如果前者更大就向 levels 添加一个空列表;
2. 将当前节点插入到对应层的列表 levels\[level\] 中;
3. 递归非空的孩子节点:helper(node.left / node.right, level + 1)。
/**
-
Definition for a binary tree node.
-
function TreeNode(val) {
-
this.val = val;
-
this.left = this.right = null;
-
}
*/
/**
-
@param {TreeNode} root
-
@return {number[][]}
*/
const levelOrder = function (root) {
const levels = [];
if (!root) {
return levels;
}
const helper = function (node, level) {
if (levels.length === level) {
levels.push([]);
}
levels[level].push(node.val);
if (node.left) {
helper(node.left, level + 1);
}
if (node.right) {
helper(node.right, level + 1);
}
};
helper(root, 0);
return levels;
};
**复杂度分析**
* 时间复杂度:O(n)O(n)
因为每个节点恰好会被运算一次。
* 空间复杂度:O(n)O(n)
保存输出结果的数组包含 nn 个节点的值。
### []( )方法二 迭代
**思路**
我们将树上顶点按照层次依次放入队列结构中,队列中元素满足 FIFO(先进先出)的原则。使用了 js 中的 push 和 shift 方法
第 0 层只包含根节点 root ,算法实现如下:
初始化队列只包含一个节点 root 和层次编号 0 : level = 0。 当队列非空的时候:
* 在输出结果 levels 中插入一个空列表,开始当前层的算法。
* 计算当前层有多少个元素:等于队列的长度。
* 将这些元素从队列中弹出,并加入 levels 当前层的空列表中。
* 将他们的孩子节点作为下一层压入队列中。
* 进入下一层 level++。
/**
-
Definition for a binary tree node.
-
function TreeNode(val) {
-
this.val = val;
-
this.left = this.right = null;
-
}
*/
/**
-
@param {TreeNode} root
-
@return {number[][]}
*/
const levelOrder = function (root) {
const levels = [];
if (!root) {
return levels;
}
let level = 0;
const queue = [root];
while (queue.length) {
// 开始当前层级的循环
levels.push([]);
// 获取当前level的长度
const levelLength = queue.length;
for (let i = 0; i < levelLength; i++) {
const node = queue.shift();
// 给当前level添加值
levels[level].push(node.val);
// 给当前level添加子节点
// 并加入队列,给下一个level使用
if (node.left) {
queue.push(node.left);
}
if (node.right) {
queue.push(node.right);
}
}
// 进入下一个level
level += 1;
}
return levels;
};
**复杂度分析**
* 时间复杂度:O(n)O(n)
* 空间复杂度:O(n)O(n)
[]( )二叉树的序列化与反序列化
------------------------------------------------------------------------
序列化是将一个数据结构或者对象转换为连续的比特位的操作,进而可以将转换后的数据存储在一个文件或者内存中,同时也可以通过网络传输到另一个计算机环境,采取相反方式重构得到原数据。
请设计一个算法来实现二叉树的序列化与反序列化。这里不限定你的序列 / 反序列化算法执行逻辑,你只需要保证一个二叉树可以被序列化为一个字符串并且将这个字符串反序列化为原始的树结构。
**示例**
你可以将以下二叉树:
1
/ \
2 3
/ \
4 5
序列化为 “[1, 2, 3, null, null, 4, 5]”
### []( )方法一 深度优先遍历(DFS)
**思路**
遍历二叉树:L、D、R 分别表示遍历左子树、访问根结点和遍历右子树。先序遍历二叉树的顺序是 DLR,中序遍历二叉树的顺序是 LDR,后序遍历二叉树的顺序是 LRD。 深度优先遍历包含先序、中序、后序遍历。先序遍历的顺序:先访问根节点,再遍历左节点,最后遍历右节点。为了能够反序列化,在遍历的时候需要将 null 也保存进去。伪代码如下:
你可以将以下二叉树:
1
/ \
2 3
/ \
4 5
序列化为 “[1, 2, 3, null, null, 4, 5]”
示例中的二叉树以本算法先序遍历后的结果为:\[1,2,null,null,3,4,null,null,5,null,null\] 反序列化:将先序遍历的结果按根节点、左子树、右子树顺序复原,伪代码如下:
structure();
node = new TreeNode();
node.left = structure();
node.right = structure();
**详解**
1. 首先序列化二叉树,定义一个遍历方法,先访问根节点,再遍历左节点,最后遍历右节点,将 null 也保存进数组中
2. 反序列化二叉树,将数组还原回二叉树,因为数组是先序遍历的结果,遍历数组,然后按照根节点、左子树、右子树顺序复原二叉树
/**
-
二叉树节点构造函数
-
@param {string} val 节点值
*/
function TreeNode (val) {
this.val = val;
this.left = this.right = null;
}
/**
-
序列化二叉树,将二叉树转成数组
-
@param {TreeNode} root
*/
const serialize = function (root) {
const result = [];
// 遍历节点
function traverseNode (node) {
if (node === null) {
result.push(null);
} else {
// 先序遍历,先访问根节点,再遍历左节点,最后遍历右节点
result.push(node.val);
traverseNode(node.left);
traverseNode(node.right);
}
}
traverseNode(root);
return result;
};
/**
-
反序列化二叉树,将数组还原回二叉树
-
@param {string} data
*/
const deserialize = function (data) {
const length = data.length;
if (length === 0) {
return null;
}
let i = 0;
// data 结构化成树
function structure () {
if (i >= length) {
return null;
}
const val = data[i];
i++;
if (val === null) {
return null;
}
const node = new TreeNode(val);
node.left = structure();
node.right = structure();
return node;
}
return structure();
};
**复杂度分析**
* 时间复杂度:O(n)O(n)
* 空间复杂度:O(n)O(n)
serialize 方法为 result 分配了空间,递归中没有产生新的对象,空间复杂度为 O(1)O(1) deserialize 方法每次遍历会 new 一个对象,空间复杂度为 O(n)O(n)
### []( )方法二 广度优先遍历(BFS)
示例中的二叉树以本算法广度优先(层序)遍历后的结果为:\[1,2,3,null,null,4,5\]
**思路**
序列化:广度优先遍历序列化二叉树是按层级从上往下将每层节点从左往右依次遍历,用队列来处理遍历,先将根节点入队,然后根节点出队,再左子树和右子树入队,递归遍历即可。 反序列化:从序列化好的数组中取第一个元素,生成根节点。将根节点放入队列。循环队列,根节点的左右子树分别放入队列,循环此操作,直到队列为空。
**详解**
序列化: 1. 定义一个 result 数组存放序列化结果 1. 定义一个 queue 数组,作为队列 2. 将根节点入队 3. 循环队列,队列中的第一个元素(节点)出队,将此节点值 push 进 result 数组。分别将此节点左右节点入队 4. 当队列为空时,跳出循环 5. 返回 result
反序列化: 1. 从 result 取出第一个节点值,生成根节点放到队列中 2. 循环队列,队列中第一个元素(节点)出队,从 result 取出下一个值还原左节点,将此左节点入队,从 result 取出下一个值还原右节点,将此右节点入队 3. 当 result 或队列为空时,跳出循环 4. 返回反序列化好的节点(根节点)
/**
-
二叉树节点构造函数
-
@param {string} val 节点值
*/
function TreeNode (val) {
this.val = val;
this.left = this.right = null;
}
/**
-
序列化二叉树,将二叉树转成数组
-
@param {TreeNode} root
*/
const serialize = function (root) {
if (!root) {
return [];
}
const result = [];
const queue = [];
queue.push(root);
let node;
while (queue.length) {
node = queue.shift();
result.push(node ? node.val : null);
if (node) {
queue.push(node.left);
queue.push(node.right);
}
}
return result;
};
/**
-
反序列化二叉树,将数组还原回二叉树
-
@param {string} data
*/
const deserialize = function (data) {
const length = data.length;
if (length === 0) {
return null;
}
// 取出第一个节点值,生成根节点放到队列中
const root = new TreeNode(data.shift());
const queue = [root];
while (queue.length) {
// 取出将要复原的节点
const node = queue.shift();
自学几个月前端,为什么感觉什么都没学到??
这种现象在很多的初学者和自学前端的同学中是比较的常见的。
因为自学走的弯路是比较的多的,会踩很多的坑,学习的过程中是比较的迷茫的。
最重要的是,在学习的过程中,不知道每个部分该学哪些知识点,学到什么程度才算好,学了能做什么。
很多自学的朋友往往都是自己去找资料学习的,资料上有的或许就学到了,资料上没有的或许就没有学到。
这就会给人一个错误的信息就是,我把资料上的学完了,估计也-就差不多的了。
但是真的是这样的吗?非也,因为很多人找的资料就是很基础的。学完了也就是掌握一点基础的东西。分享给你一份前端分析路线,你可以参考。
还有很多的同学在学习的过程中一味的追求学的速度,很快速的刷视频,写了后面忘了前面,最后什么都没有学到,什么都知道,但是什么都不懂,要具体说,也说不出个所以然。
所以学习编程一定要注重实践操作,练习敲代码的时间一定要多余看视频的时间。