平衡二叉树+所有路径(涉及回溯)+左叶子之和(day17*)

LC110 平衡二叉树

给定一个二叉树,判断它是否是高度平衡的二叉树。

递归解法:

class Solution:
    def isBalanced(self, root: Optional[TreeNode]) -> bool:
        def cal_height(root) -> int:
            if not root: return 0
            return max(cal_height(root.left), cal_height(root.right)) + 1

        if not root: return True
        m = cal_height(root.left)
        n = cal_height(root.right)
        if abs(m - n) > 1:
            return False
        return self.isBalanced(root.left) and self.isBalanced(root.right)

LC 257. 二叉树的所有路径 

给你一个二叉树的根节点root ,按任意顺序 ,返回所有从根节点到叶子节点的路径。

这一题需要用到回溯,也是我第一次接触,很有意思。

尤其注意里面的path[:],不加后面的[:]会出问题,不加把path的地址传到paths里了,而加了[:]才是传具体的元素。

class Solution:
    def binaryTreePaths(self, root: Optional[TreeNode]) -> List[str]:
        # 思路:先序遍历
        # 1. 定义函数参数和返回值:没有返回值,不断在paths里添加路径
        path = []
        paths = []
        def construct_path(root):
            # 2. 定义结束条件:
            if not root:
                return
            path.append(root.val)
            # 3. 递归时的操作
            # 如果是叶子节点,加入到paths中
            if not root.left and not root.right:
                paths.append(path[:]) # 注意一定要加[:]
                return
            # 左右孩子加入路径,返回上一级要回溯;
            if root.left:
                construct_path(root.left)
                path.pop()
            if root.right:
                construct_path(root.right)
                path.pop()

        construct_path(root)
        # 第一次我写了两个for循环T_T。可以利用两个小技巧美化代码
        # 1. map可以根据提供的函数对指定序列做映射;
        # 2. for循环的一行代码形式的写法
        ans = ['->'.join(map(str,i)) for i in paths]
        return ans

LC 404 左叶子之和

给定二叉树的根节点 root ,返回所有左叶子之和。

思路1 每次遇到左叶子节点就往总和s上加。

class Solution:
    def sumOfLeftLeaves(self, root: Optional[TreeNode]) -> int:        
        def isLeftLeaf(root):
            return root and not root.left and not root.right
            
        ans = 0
        def dfs(root):
            nonlocal ans # 不加nonlocal会报错:local variables 'ans' referenced before assignment
            #终止条件
            if not root: return 0
            if isLeftLeaf(root.left):
                ans = ans + root.left.val #根
            dfs(root.left)  # 左
            dfs(root.right) # 右
        
        dfs(root)
        return ans

至于为什么要写nonlocal,因为外部的ans在inner scope里面不可以reassignment。而如果ans=[],内部进行ans.append(1)是可以的,因为[]是mutable的,改了之后没有进行reassignment。

可以参照上面一题中的paths.append(path[:])。首先,按照上面的解释,paths.append是可以修改外部的paths的。问题又来了,为什么[:]不能省略呢?因为下一次如果path指向的list改变了,那么之前加进来的path也跟着变化了,加了[:]就表示具体的元素。

下面两个链接是这个问题的很好的回答,mark。

https://stackoverflow.com/questions/64323757/why-does-python-3-8-0-allow-to-change-mutable-types-from-enclosing-function-scop

Facts and myths about Python names and values | Ned Batchelder

思路2 某个节点的左叶子之和就是左孩子的左叶子之和➕右孩子的左叶子之和。如果左孩子是结点,加上它自己的值。

class Solution:
    def sumOfLeftLeaves(self, root: Optional[TreeNode]) -> int:        
        def isLeftLeaf(root):
            return root and not root.left and not root.right

        if not root: return 0
        m = self.sumOfLeftLeaves(root.left)
        n = self.sumOfLeftLeaves(root.right)

        return m+n+root.left.val if isLeftLeaf(root.left) else m + n

你可能感兴趣的:(leetcode,算法)