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


前言

513.找树左下角的值

在这里插入图片描述

思路

本题掌握迭代法和递归法
迭代法十分直接

方法一 迭代法

层序遍历:输出最后一层的第一个叶子节点就行
具体实现:与层序遍历的区别在于,不需要保存每一层的每一个节点,只需要用一个值result保存每一层的第一个节点,反正result每一层都会被覆盖,直到最后一层;

from collections import deque
class Solution:
    def findBottomLeftValue(self, root):
        if root is None:
            return 0
        queue = deque()
        queue.append(root)
        result = 0
        while queue:
            size = len(queue)
            for i in range(size):
                node = queue.popleft()
                if i == 0:
                    result = node.val
                if node.left:
                    queue.append(node.left)
                if node.right:
                    queue.append(node.right)
        return result

方法二 递归法

两种方法都需要掌握
这个题目无论那种顺序都是不处理中节点的
总体思路:深度最大的叶子节点一定是最后一行;定义全局变量depth-max和result,如果有发现大于这个深度的,就更新depth-max和对应的result;

思路依据:无论哪种顺序的遍历,一定是先遍历左边,所以哪怕后面有相同深度的,result里面保存的也是最左边的。只有当深度大于已有的最大深度了,才会更新result

递归三部曲:

  1. 返回和输入:需要定义一个int来记录最长深度,还有一个result对应该深度的val
  2. 迭代终止:如果发现叶子节点了,比较更新max-depth和对应的result
  3. 单层迭代逻辑:如果有左节点,depth++,然后进入递归【先计数层数,再递归进入的】,然后记得depth–【回溯】,因为后面要进入右边
    • 它 的整体逻辑是,当前遍历的是depth,在遍历过程中不断++和–【回溯】。原本就为1,先++,然后再进入新递归,然后左边递归会一直到叶子节点更新max-depth之后回来,然后–,进入右边。
      在这里插入图片描述
      这个代码要重新写,稍微修改了代码,教程里面result初始化为none是不对的,需初始化为root.val

class Solution(object):

    def traversal(self,root,depth):
        if not root.left and not root.right:#叶子节点
            if depth > self.max_depth:
                self.max_depth = depth
                self.result = root.val
            return 
        if root.left:

            depth += 1
            self.traversal(root.left,depth)
            depth -= 1
        if root.right:
            depth += 1
            self.traversal(root.right,depth)
            depth -= 1
    def findBottomLeftValue(self, root):
        """
        :type root: TreeNode
        :rtype: int
        """
        if not root:return 

        self.max_depth = 1
        self.result = root.val
        depth = 1
        
        self.traversal(root,depth)
        return self.result

112. 路径总和 113.路径总和ii

在这里插入图片描述

思路

优先掌握递归法
这个题目无法用何种方法遍历都是不处理中节点的

方法一

思路:和下面我的方法区别在于,是从target开始减,如果减到叶子节点为0,那么就说明是有的;
此外,直接返回了true和false
具体实现:和513类似,先减去了要进入的节点的值,再进入递归。[可以看三部曲,这里不放了]

注意精简版本中:

            if self.traversal(cur.left, count): # 递归,处理节点
                return True
            count += cur.left.val # 回溯,撤销处理结果

可以写成if traversal(root.left,count-root.left.val): return True

class Solution:
    def traversal(self, cur: TreeNode, count: int) -> bool:
        if not cur.left and not cur.right and count == 0: # 遇到叶子节点,并且计数为0
            return True
        if not cur.left and not cur.right: # 遇到叶子节点直接返回
            return False
        
        if cur.left: # 左
            count -= cur.left.val
            if self.traversal(cur.left, count): # 递归,处理节点
                return True
            count += cur.left.val # 回溯,撤销处理结果
            
        if cur.right: # 右
            count -= cur.right.val
            if self.traversal(cur.right, count): # 递归,处理节点
                return True
            count += cur.right.val # 回溯,撤销处理结果
            
        return False
    
    def hasPathSum(self, root: TreeNode, sum: int) -> bool:
        if root is None:
            return False
        return self.traversal(root, sum - root.val) 
#精简版本
class Solution:
    def hasPathSum(self, root: TreeNode, sum: int) -> bool:
        if not root:
            return False
        if not root.left and not root.right and sum == root.val:
            return True
        return self.hasPathSum(root.left, sum - root.val) or self.hasPathSum(root.right, sum - root.val)

方法一2 迭代法(自己写的)

我听课之前自己写了一个,递归+回溯,sum到叶子节点如果等于的话就是【总体思路跟513一样】;
思路是:先加上,再进入递归。

class Solution(object):
    def traversal(self,root):

        
        if not root.left and not root.right and self.sum == self.targetSum:
            return 1
        if not root.left and not root.right and self.sum != self.targetSum:
            return 0 
           
        if root.left:
            self.sum += root.left.val
            re = self.traversal(root.left)
            if re == 1: return 1
            self.sum -= root.left.val

        if root.right:
            self.sum += root.right.val
            re = self.traversal(root.right)
            if re == 1: return 1
            self.sum -= root.right.val
        return 0 
            
        
    def hasPathSum(self, root, targetSum):
        """
        :type root: TreeNode
        :type targetSum: int
        :rtype: bool
        """
        if root == None: return False
        self.targetSum = targetSum
        self.sum = root.val
        re = self.traversal(root)
        return True if re == 1 else False

方法二 递归法

113

💥result append的时候一定要append一个self.path[:] 复值的值

class Solution:
    def __init__(self):
        self.result = []
        self.path = []

    def traversal(self, cur, count):
        if not cur.left and not cur.right and count == 0: # 遇到了叶子节点且找到了和为sum的路径
            self.result.append(self.path[:])
            return

        if not cur.left and not cur.right: # 遇到叶子节点而没有找到合适的边,直接返回
            return

        if cur.left: # 左 (空节点不遍历)
            self.path.append(cur.left.val)
            count -= cur.left.val
            self.traversal(cur.left, count) # 递归
            count += cur.left.val # 回溯
            self.path.pop() # 回溯

        if cur.right: #  右 (空节点不遍历)
            self.path.append(cur.right.val) 
            count -= cur.right.val
            self.traversal(cur.right, count) # 递归
            count += cur.right.val # 回溯
            self.path.pop() # 回溯

        return

    def pathSum(self, root, targetSum):
        # self.result.clear()
        # self.path.clear()
        if not root:
            return self.result
        self.path.append(root.val) # 把根节点放进路径
        self.traversal(root, targetSum - root.val)
        return self.result 
        

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

后序和前序无法唯一地确定一个二叉树

思路

总体思路:由于是构造二叉树,所以每一次递归都是构建节点
在这里插入图片描述
说到一层一层切割,就应该想到了递归。

来看一下一共分几步:

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

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

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

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

第五步:切割后序数组,切成后序左数组和后序右数组【依据中序遍历的左边数组的大小来切,左边切出来了右边就有了】

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

递归三部曲

  1. 传入的是中序和后序
  2. 到了叶子节点,应该return这个节点
  3. 单层中,知道了根节点之后new一个node。然后给中序和后序分出一个左边和右边,分别进入递归,递归的结果就是左孩子和哟哟孩子

在这里插入图片描述

方法一 递归

自己写的注意事项:
1.如何判断到了叶子节点了:当前这一次递归中传入的posorder或者inorder的长度为1,那么就return 左右孩子为none的new的一个node
2. 切割后序遍历的左右的时候,一定要记得去除最后一个元素,那个是根节点使用[x:-1]的方法,不包括最后一个元素
3. 切片不算stop

# Definition for a binary tree node.
# class TreeNode(object):
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution(object):
    # def traversal(self,):

    def buildTree(self, inorder, postorder):
        """
        :type inorder: List[int]
        :type postorder: List[int]
        :rtype: TreeNode
        """

        # 第一步: 特殊情况讨论: 树为空. (递归终止条件)
        if not postorder:
            return None

        # 第二步: 后序遍历的最后一个就是当前的中间节点.
        root_val = postorder[-1]
        root = TreeNode(root_val)

        # 第三步: 找切割点.
        separator_idx = inorder.index(root_val)

        # 第四步: 切割inorder数组. 得到inorder数组的左,右半边.
        inorder_left = inorder[:separator_idx]
        inorder_right = inorder[separator_idx + 1:]

        # 第五步: 切割postorder数组. 得到postorder数组的左,右半边.
        # ⭐️ 重点1: 中序数组大小一定跟后序数组大小是相同的.
        postorder_left = postorder[:len(inorder_left)]
        postorder_right = postorder[len(inorder_left): - 1]

        # 第六步: 递归
        root.left = self.buildTree(inorder_left, postorder_left)
        root.right = self.buildTree(inorder_right, postorder_right)
         # 第七步: 返回答案
        return root
        

方法二

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

思路

也是一样的思路,只不过从最后一个元素为根节点变成,第一个元素为根节点

class Solution(object):
    def buildTree(self, preorder, inorder):
        """
        :type preorder: List[int]
        :type inorder: List[int]
        :rtype: TreeNode
        """
        if not inorder: return
        root_val = preorder[0]
        node = TreeNode(root_val)
        if len(preorder) ==1:return node
        seperate_index = inorder.index(root_val)
        #seperate inorder
        inorder_left = inorder[:seperate_index]
        inorder_right = inorder[seperate_index+1:]
        # seperat preorder
        preorder_left = preorder[1:len(inorder_left)+1]
        preorder_right = preorder[len(inorder_left)+1:]
        node.left = self.buildTree(preorder_left,inorder_left)
        node.right = self.buildTree(preorder_right,inorder_right)
        return node

总结

  • 11
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
代码随想录算法训练营是一个优质的学习和讨论平台,提供了丰富的算法训练内容和讨论交流机会。在训练营中,学员们可以通过观看视频讲解来学习算法知识,并根据讲解内容进行刷题练习。此外,训练营还提供了刷题建议,例如先看视频、了解自己所使用的编程语言、使用日志等方法来提高刷题效果和语言掌握程度。 训练营中的讨论内容非常丰富,涵盖了各种算法知识点和解题方法。例如,在第14训练营中,讲解了二叉树的理论基础、递归遍历、迭代遍历和统一遍历的内容。此外,在讨论中还分享了相关的博客文章和配图,帮助学员更好地理解和掌握二叉树的遍历方法。 训练营还提供了每日的讨论知识点,例如在第15的讨论中,介绍了层遍历的方法和使用队列来模拟一层一层遍历的效果。在第16的讨论中,重点讨论了如何进行调试(debug)的方法,认为掌握调试技巧可以帮助学员更好地解决问题和写出正确的算法代码。 总之,代码随想录算法训练营是一个提供优质学习和讨论环境的平台,可以帮助学员系统地学习算法知识,并提供了丰富的讨论内容和刷题建议来提高算法编程能力。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [代码随想录算法训练营每日精华](https://blog.csdn.net/weixin_38556197/article/details/128462133)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值