代码随想录算法训练营第十八天|二叉树part5

128 阅读4分钟

找树左下角的值

题目链接

leetcode.cn/problems/fi…

第一想法

层序遍历,直接取最后一层的值

思路

1.递归

找深度最大的叶子节点

递归三部曲:

  • 确定递归函数的参数和返回值

    这里的参数是root,不需要返回值,用两个变量分别记录最大深度和最大深度左节点的值

  • 确定终止条件

    遇到叶子节点就统计一下深度

  • 确定单层递归逻辑

代码如下:

var findBottomLeftValue = function(root) {
    let maxPath = 0, resNode = null;
    // 确定参数
    function getnode(node,path){
         // 确定终止逻辑
         if(node.left === null && node.right === null){
             if(path > maxPath){
                 maxPath = path;
                 resNode = node.val;
             }
         }
         node.left && getnode(node.left, path+1);
         node.right && getnode(node.right, path+1);
    }
    getnode(root,1);
    return resNode;
​
};

2.层序遍历

var findBottomLeftValue = function(root) {
    let queue = [] , res = null;
    queue.push(root);
   
    while(queue.length !== 0) {
        // 记录当前层级节点数,方便之后以此弹出
        let length = queue.length;
        //存放每一层的节点
        let curLevel = [];
        for(let i = 0;i < length; i++) {
            let node = queue.shift();
            // 存放当前弹出的节点
            curLevel.push(node.val);
            // 存放当前层下一层的节点
            node.left && queue.push(node.left);
            node.right && queue.push(node.right);
        }
        //把每一层的结果放到结果数组
            res = curLevel[0];
​
    }
    return res;
​
};

每层的循环结束后,将改层的第一个值赋值给res

最终,res即为最后一层的左下角的值

总结

这道题两种方法都可以做,但明显,层序遍历更加简单

112.路径总和

题目链接

leetcode.cn/problems/pa…

第一想法

思路

  • 确定递归函数的参数和返回值

    这里需要两个变量,节点,以及计数器来判断目前的和

    返回值就是true or false

  • 确定终止条件

    遍历到了叶子节点并且sum == target,这里用相加比较难比较,可以直接用相减的办法

  • 确定单层递归逻辑

    如果满足条件,立刻返回true,否则,回溯

代码逻辑如下:

var hasPathSum = function(root, targetSum) {
    const travel = (node, sum) => {
        if(sum === 0 && node.left === null && node.right === null) return true;
        if(node.left === null && node.right === null) return false;
        // 左边
        if(node.left && travel(node.left, sum - node.left.val)) return true;
        // 右边
        if(node.right && travel(node.right,sum - node.right.val)) return true;
        return false;
    }
    if(!root) return false;
    return travel(root, targetSum - root.val);
};

精简版本:

var hasPathSum = function(root, targetSum) {
    if(!root) return false;
    if(root.left === null && root.right === null && root.val == targetSum) return true;
    return hasPathSum(root.left, targetSum - root.val) || hasPathSum(root.right, targetSum-root.val);
};

总结

代码逻辑需要理清楚,想明白逻辑之后,用精简版本会方便很多

113.路径总和2

题目链接

leetcode.cn/problems/pa…

第一想法

需要在每次迭代应该用数组存储路径

思路

var pathSum = function(root, targetSum) {
 //递归方法
    let respath = [],curpath = [];
    // 1. 确定递归函数参数
    const traveltree = function(node,count) {
        curpath.push(node.val);
        count -= node.val;
        if(node.left === null && node.right === null && count === 0) {
            respath.push([...curpath]);
        }
        node.left && traveltree(node.left, count);
        node.right && traveltree(node.right, count);
        let cur = curpath.pop();
        count -= cur;
    }
    if(root === null) {
        return respath;
    }
    traveltree(root, targetSum);
    return respath;
};

106.从中序与后序遍历序列构造二叉树

题目链接

leetcode.cn/problems/co…

第一想法

没思路

思路

步骤如下:

  • 第一步:如果数组大小为零的话,说明是空节点了。
  • 第二步:如果不为空,那么取后序数组最后一个元素作为节点元素。
  • 第三步:找到后序数组最后一个元素在中序数组的位置,作为切割点
  • 第四步:切割中序数组,切成中序左数组和中序右数组 (顺序别搞反了,一定是先切中序数组)
  • 第五步:切割后序数组,切成后序左数组和后序右数组(利用二者的长度应该是相等的这一特点来切割)
  • 第六步:递归处理左区间和右区间

切割时,需要注意的是边界值,到底是左开右闭还是左闭右闭等

中序数组的切割统一用左闭右开(js中的slice就是左闭右开,比较方便)

后序数组如何切割?注意,后序数组的大小和中序数组一定是相同的,因此,得到中序的左右数组后,可以根据大小来切割后序数组

var buildTree = function(inorder, postorder) {
    if(inorder.length == 0) return null;
    let rootVal = postorder.pop();
    // 确定根节点的位置
    let pos = inorder.indexOf(rootVal);
    // 对前序遍历进行处理
    // 对左子树进行切割
    let leftTree_in = inorder.slice(0,pos);
    // 对右子树进行切割
    let rightTree_in = inorder.slice(pos+1);
    // 对后序遍历进行处理
    let leftTree_post = postorder.slice(0,pos);
    let rightTree_post = postorder.slice(pos);
    // 创建子树
    let root = new TreeNode(rootVal);
    // 递归创建左右子树
    root.left = buildTree(leftTree_in,leftTree_post);
    root.right = buildTree(rightTree_in,rightTree_post);
    return root;
​
};

注意看一下这里代码的逻辑,实际上代码也可以再精简一些:

var buildTree = function(inorder, postorder) {
    if (!inorder.length) return null;
    const rootVal = postorder.pop(); // 从后序遍历的数组中获取中间节点的值, 即数组最后一个值
    let rootIndex = inorder.indexOf(rootVal); // 获取中间节点在中序遍历中的下标
    const root = new TreeNode(rootVal); // 创建中间节点
    root.left = buildTree(inorder.slice(0, rootIndex), postorder.slice(0, rootIndex)); // 创建左节点
    root.right = buildTree(inorder.slice(rootIndex + 1), postorder.slice(rootIndex)); // 创建右节点
    return root;
};

105.从前序与中序遍历序列构造二叉树

题目链接

leetcode.cn/problems/co…

第一想法

同样的,这里抓住前序遍历的第一个点是根节点这一点来写

整体思路和上面是差不多的

思路

var buildTree = function(preorder, inorder) {
    if(preorder.length == 0) return null;
    let nodeVal = preorder.shift();
    let pos = inorder.indexOf(nodeVal);
    let root = new TreeNode(nodeVal);
    root.left = buildTree(preorder.slice(0,pos), inorder.slice(0,pos));
    root.right = buildTree(preorder.slice(pos),inorder.slice(pos+1));
    return root;
};

总结

整体的套路可以记一下,抓住前序遍历的第一个值和后序遍历的最后一个值是根节点这一点来写

注意递归!