javascript 算法之 树 必出精品 (二)

669 阅读5分钟

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中的树组件 --实际工作

  • 使用深度优先遍历

8、总结篇