代码随想录算法训练营第十八天 | 513.找树左下角的值、112. 路径总和、106.从中序与后序遍历序列构造二叉树

LeetCode 513.找树左下角的值

题目链接🔗:513.找树左下角的值

解题思路🤔

使用层序遍历,每一层for循环的时候要用一个固定变量len记录循环次数,不能用que.length(因为这个是变化的)

遇到的问题😢

代码实现👨🏻‍💻

层序遍历

var findBottomLeftValue = function(root) {
	let queue = [];
    if(root === null) return null;
    
    queue.push(root);
    let resNode;
    
    while(queue.length) {
        let length = queue.length;
        
        for(let i = 0; i < length; i++) {
            let node = queue.shift();
            if(i === 0) resNode = node.val;
            node.left && queue.push(node.left);
            node.right && queue.push(node.right);
        }
    }
    
    return resNode;
};

总结📖


LeetCode 112. 路径总和

题目链接🔗:112. 路径总和

解题思路🤔

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

    • 参数:二叉树的根节点、计数器(用来计算二叉树的一条边之和是否正好是目标和)
    • 返回值:遍历的路线,并不要遍历整棵树,所以递归函数需要返回值,可以用bool类型表示
  2. 确定终止条件

    使用递减,让计数器初始化为目标和,每次减去遍历路径节点上的数值

    如果最后count == 0,同时到了叶子节点时说明找到了目标和

    如果到了叶子节点,计数器不为0,就是没找到

  3. 确定单层递归的逻辑

    终止条件是判断叶子节点,所以递归的过程中就不要让空节点进入递归了。

    递归函数是有返回值的,如果递归函数返回true,说明找到了合适的路径,应该立刻返回。

遇到的问题😢

代码实现👨🏻‍💻

递归法

var hasPathSum = function(root, targetSum) {
	const traversal = (node, cnt) => {
    // 遇到叶子节点,并且计数为0
    if (cnt === 0 && !node.left && !node.right) return true;
    
    // 遇到叶子节点而没有找到合适的边(计数不为0),直接返回
    if (!node.left && !node.right) return false;

    //  左(空节点不遍历).遇到叶子节点返回true,则直接返回true
    if (node.left && traversal(node.left, cnt - node.left.val)) return true;
    
    //  右(空节点不遍历)  
    if (node.right && traversal(node.right, cnt - node.right.val)) return true;
    
    return false;
  };
  
  if (!root) return false;
  
  return traversal(root, targetSum - root.val);
};

总结📖

递归法

判断是否需要返回值的三种情况:

  • 如果需要搜索整棵二叉树且不用处理递归返回值,递归函数就不要返回值。(113.路径总和ii)
  • 如果需要搜索整棵二叉树且需要处理递归返回值,递归函数就需要返回值。 (236. 二叉树的最近公共祖先)
  • 如果要搜索其中一条符合条件的路径,那么递归一定需要返回值,因为遇到符合条件的路径了就要及时返回。(112. 路径总和)

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

题目链接🔗:106.从中序与后序遍历序列构造二叉树

解题思路🤔

第一步:如果数组大小为零的话,说明是空节点了。

第二步:如果不为空,那么取后序数组最后一个元素作为节点元素。

第三步:找到后序数组最后一个元素在中序数组的位置,作为切割点

第四步:切割中序数组,切成中序左数组和中序右数组 (顺序别搞反了,一定是先切中序数组)

第五步:切割后序数组,切成后序左数组和后序右数组

第六步:递归处理左区间和右区间

  • 切割中序数组:
    切割点在后序数组的最后一个元素,就是用这个元素来切割中序数组的,所以必要先切割中序数组。同时要坚持循环不变量。
  • 切割后序数组:
    • 使用左闭右开时,首先后序数组的最后一个元素指定不能要了,这是切割点 也是 当前二叉树中间节点的元素,已经用了。
    • 后序数组没有明确的切割元素来进行左右切割,不像中序数组有明确的切割点,切割点左右分开就可以了。
    • 中序数组大小一定是和后序数组的大小相同的
    • 中序数组我们都切成了左中序数组和右中序数组了,那么后序数组就可以按照左中序数组的大小来切割,切成左后序数组和右后序数组。

遇到的问题😢

代码实现👨🏻‍💻

代码实现

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;
};

总结📖


今日收获

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值