1、leetcode 104 二叉树最大深度
- 道理我们都懂 来 我们开始操作一下吧
/**
* @param {TreeNode} root
* @return {number}
* 1、新建变量 存放层级 deep
* 2、深度优先遍历
* 3、刷新 deep值 传入参数 l 先拿到每一层的deep 初始为1
* 4、拿到层级最大值 Math.max(deep,l)
*/
var maxDepth = function(root) {
// 新建变量记录层级
var deep = 0;
// 深度优先遍历 dfs函数
var dfs = function (n,l) {
if (!n) {
return
}
// 先访问当前节点
// console.log(n.val,l)
// 拿到层级比较大的结果
//判断一下 叶子节点才计算最大深度 节省性能
if(!n.left && !n.right){
deep = Math.max(deep,l)
}
//访问左节点
dfs(n.left,l+1)
//访问右节点
dfs(n.right,l+1)
}
// 1 表示传入的默认层级 初始化 层级是 1
dfs(root,1) // 调用根节点
return deep
};
- 步骤也很清楚 上面是思路部分
- 走 喝杯卡布奇诺
时间复杂度 O(n) 空间复杂度 递归嵌套 形成堆栈 最好的情况 O(logn) 最坏情况 O(n) 不分叉
2、leetcode 111 二叉树最小深度
- 这个怎么解决?
- 上代码
/**
* @param {TreeNode} root
* @return {number}
* 注意 这个地方需要注意 广度优先遍历获取每个层级的方式是改成数组
* 每一块加数组 两个参数 第二个参数是 l 层级
* 时间复杂度 O(n) 最坏的情况 n表示需要循环遍历所有树的节点
* 空间复杂度 O(n) 最坏情况 队列需要装满 树中节点
*/
var minDepth = function(root) {
if(!root) {return 0}
// 新建队列 并将根节点放进去
// 里面再放一个数组 是为了 记录每一个层级 初始值为1
const q = [[root,1]]
while(q.length){
// 拿到队头 并访问
const [n,l] = q.shift()
//得到每一层层级
// console.log(n.val,l)
//遇到叶子节点(没有左节点和右节点 直接返回它的层级就是最小层级)
if(!n.left && !n.right){
return l
}
// 将它的子节点放到队列内
if(n.left) q.push([n.left,l+1])
if(n.right) q.push([n.right,l+1])
}
};
- 分解一下 第一步 广度优先遍历
var minDepth = function(root) {
if(!root){return 0}
const q = [root]
while(q.length){
const n = q.shift()
console.log(n.val)
if(n.left) q.push(n.left)
if(n.right) q.push(n.right)
}
};
- 每一层层级
var minDepth = function(root) {
if(!root){return 0}
const q = [[root,1]]
while(q.length){
const [n,l] = q.shift()
console.log(n.val,l)
if(n.left) q.push([n.left,l+1])
if(n.right) q.push([n.right,l+1])
}
};
- 找到叶子节点 求最小深度
var minDepth = function(root) {
if(!root){return 0}
const q = [[root,1]]
while(q.length){
const [n,l] = q.shift()
if(!n.left && !n.right){
return l
}
// console.log(n.val,l)
if(n.left) q.push([n.left,l+1])
if(n.right) q.push([n.right,l+1])
}
};
3、leetcode 102 层序遍历
- 来 上代码 当然思路也写出来了
/**
* @param {TreeNode} root
* @return {number[][]}
* 1、广度优先遍历
* 2、得到每一层的层级
* 3、将结果放到每一层的数组内
*/
var levelOrder = function(root) {
if(!root) {return []}
// 第一层的层级设置为0
const q = [[root,0]]
//结果数组
const res = []
while(q.length){
const [n,l] = q.shift()
// console.log(n.val,l)
if(!res[l]){ // 没有值时 将当前节点值放进数组中
res.push([n.val])
}else{ // 有值时 将每一层的节点值 放进数组
res[l].push(n.val)
}
// 广度优先遍历 将左右子树都放进队列中
if(n.left) q.push([n.left,l+1])
if(n.right) q.push([n.right,l+1])
}
return res
};
- 我们尝试 第二种 方法 简化一下
// 时间复杂度 O(n) n就是这棵树的节点数 空间复杂度 O(n) n q = []是数组 可能存放很多的元素
var levelOrder = function(root) {
if(!root){return []}
const q = [root]
const res = []
while(q.length){
// 记录 队列长度 len
var len = q.length
// 往结果数组里面放一个空数组
res.push([])
while(len--){
const n = q.shift()
// 将当前层级的每个节点放入 结果数组
res[res.length -1].push(n.val)
if(n.left) q.push(n.left)
if(n.right) q.push(n.right)
}
}
return res
};
4、 leetcode 94 二叉树的中序遍历
- 太精品了 这些都是选的二叉树相关的内容
- 学到就是赚到系列
- 简单的递归版本
需要注意 第二步 访问根节点 需要推入 结果数组res内
var inorderTraversal = function(root) {
// 递归版本
var res = []
//中序遍历函数
var ino = function(n){
if(!n) {return }
ino(n.left)
res.push(n.val) // 将节点推入 结果数组
ino(n.right)
}
ino(root) // 执行中序遍历函数 传入root
return res
};
- 这个是
面试重点 非递归版本
// 时间复杂度 O(n) 访问所有的二叉树节点 空间复杂度 O(n) n为新建的栈 stack
var inorderTraversal = function(root) {
//非递归版本
var res = []
var p = root // 需要一个指针
var stack = [] // 需要一个栈
while(stack.length || p){
while(p){
stack.push(p)
p = p.left
}
const n = stack.pop()
res.push(n.val)
p = n.right
}
return res
};
- nice 本节 你肯定 受益匪浅
- 走 我们一起找个 动漫 看看
- 或者 你有好看的动漫 推荐给我
5、leetcode 112 路径总和
- 不要紧张 这是一个 简单的问题
- 上代码 看看(貌似还写了比较多的内容)
/**
* @param {TreeNode} root
* @param {number} sum
* @return {boolean}
* 1、判断root 不存在情况
* 2、深度优先遍历 dfs
* 3、记录当前路径下 所有节点值的和
* 4、在叶子节点处进行判断 得到 结果
* 时间复杂度 O(n) n是树的节点 不管是深度优先还是广度优先都是O(n)
* 空间复杂度 没有使用数组 但是递归使用时候 会产生 函数调用堆栈
* 坏的情况是O(n) 树不分叉 偏向一边 好的情况 O(logn) 均匀成两边
*/
var hasPathSum = function(root, sum) {
if(!root) {return false}
var res = false
var dfs = function(n,s){
// console.log(n.val,s)
//叶子节点进行判断
if(!n.left && !n.right && s == sum){
res = true
}
// 遍历到当前节点 所有节点值的求和
if(n.left) dfs(n.left,s + n.left.val)
if(n.right) dfs(n.right,s+ n.right.val)
}
dfs(root,root.val)
return res
};
- nice 这个东西 应该比较好操作
6、前端与树 遍历json的所有节点值 --- 实际工作
- 当然先说明 我们使用的方法是
深度优先遍历
// 这是一个json文件 怎么遍历它的每一个节点呢 ?
// 使用深度优先遍历
const json = {
a: {
b: {
c: 1
}
},
d: [1, 2]
}
var dfs = (n) => {
console.log(n)
// 遍历 属性里的值
Object.keys(n).forEach(k => {
dfs(n[k])
})
}
dfs(json)
-
我们尝试 看一下 结果
-
貌似 还可以
但是我怎么知道 每个值对应哪个节点呢 ? -
不要慌 我们再来操作 一波
-
我们加了一个 参数 path
// 这是一个json文件 怎么遍历它的每一个节点呢 ?
// 使用深度优先遍历
const json = {
a: {
b: {
c: 1
}
},
d: [1, 2]
}
var dfs = (n, path) => {
console.log(n, path)
// 遍历 属性里的值
Object.keys(n).forEach(k => {
dfs(n[k], path.concat(k))
})
}
dfs(json, [])
-
打印结果 非常神奇
-
这样可以很清楚的看到 每一个节点和对象的对应关系 很nice
-
这个东西 在实际工作 很常见
后端传来的json 数据 往往需要过滤后才能使用
7、前端与树 渲染 Antd中的树组件 --实际工作
- 使用深度优先遍历