[算法学习09]二叉树基础

前言

以下文字来自灵神视频下的评论

1. 如何思考二叉树相关问题?
- 不要一开始就陷入细节,而是思考整棵树与其左右子树的关系。
2. 为什么需要使用递归?
- 子问题和原问题是相似的,他们执行的代码也是相同的(类比循环),但是子问题需要把计算结果返回给上一级,这更适合用递归实现。
3. 为什么这样写就一定能算出正确答案?
- 由于子问题的规模比原问题小,不断“递”下去,总会有个尽头,即递归的边界条件 ( base case ),直接返回它的答案“归”;
- 类似于数学归纳法(多米诺骨牌),n=1时类似边界条件;n=m时类似往后任意一个节点
4. 计算机是怎么执行递归的?
- 当程序执行“递”动作时,计算机使用栈保存这个发出“递”动作的对象,程序不断“递”,计算机不断压栈,直到边界时,程序发生“归”动作,正好将执行的答案“归”给栈顶元素,随后程序不断“归”,计算机不断出栈,直到返回原问题的答案,栈空。
5. 另一种递归思路
- 维护全局变量,使用二叉树遍历函数,不断更新全局变量最大值。

1. Leetcode 104. 二叉树的最大深度 https://leetcode.cn/problems/maximum-depth-of-binary-tree/solutions/2010612/kan-wan-zhe-ge-shi-pin-rang-ni-dui-di-gu-44uz/

解题思路:

把问题拆小! 二叉树的最大深度是什么? 答案是,左右子树的最大深度取max +1。

边界如何处理?

很简单! 根节点为空时返回0即可!

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution:
    def maxDepth(self, root: Optional[TreeNode]) -> int:
        if root is None:
            return 0

        # 左子树最大深度        
        left_m = self.maxDepth(root.left)
        # 右子树最大深度 
        right_m = self.maxDepth(root.right)

        return max(left_m, right_m) + 1

2. Leetcode111. 二叉树的最小深度 https://leetcode.cn/problems/minimum-depth-of-binary-tree/

给定一个二叉树,找出其最小深度。

最小深度是从根节点到最近叶子节点的最短路径上的节点数量。

解题思路:

最小深度是从根节点到最近叶子节点的最短路径上的节点数量。

首先,将问题拆分。 求二叉树的最小深度就是 求左右子树的最小深度,取min后+1。

然后,处理边界, if root is None: return inf  空节点不是我们想要的内容

if root.left is None and root.right is None: return 1 叶子节点才是我们递归的真正终点,返回它本身的高度1。

此外,上述内容应该定义在函数中。 因为传入的树可能是一颗空树,所以 if root is None: return 0

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution:
    def minDepth(self, root: Optional[TreeNode]) -> int:
        if root is None:
            return 0

        def minD(root):
            if root is None:
                return inf
            if root.left is None and root.right is None:
                return 1 
            
            left_m = minD(root.left)
            right_m = minD(root.right)
            return min(left_m, right_m) + 1
        return minD(root)

3. Leetcode 112. 路径总和 https://leetcode.cn/problems/path-sum/

给你二叉树的根节点 root 和一个表示目标和的整数 targetSum 。判断该树中是否存在 根节点到叶子节点 的路径,这条路径上所有节点值相加等于目标和 targetSum 。如果存在,返回 true ;否则,返回 false 。

叶子节点 是指没有子节点的节点。

解题思路:

首先,拆解问题。 求root->叶子节点的路径,要求路径之和 == targetsum。

求root->叶子节点的路径 ==> 左右子树是否存在到叶子的路径,要求路径之和 == targetsum - root.val。

然后,处理边界。

if root is None: return False 如果传进来是空树,那么不存在路径,return False

if root.left is None and root.right is None: return targetsum == root.val 

如果是叶子节点,那么只需要判断一下 经过多次减法的targetsum 和 root.val 是否相等

以下是代码

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution:
    def hasPathSum(self, root: Optional[TreeNode], targetSum: int) -> bool:
        if root is None:
            return False
        if root.left is None and root.right is None:
            return targetSum == root.val 
        return self.hasPathSum(root.left, targetSum - root.val) or self.hasPathSum(root.right, targetSum - root.val)

4. Leetcode 113. 路径总和 II https://leetcode.cn/problems/path-sum-ii/

解题思路:

和上题的思路是类似的,只不过我们要额外定义两个数组,path和result。 前者用来存经过的路径,后者是如果满足路径和==targetSum,就把path加入我们的result。

以下是代码

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution:
    def pathSum(self, root: Optional[TreeNode], targetSum: int) -> List[List[int]]:
        res, path = [], []
        def getRes(root, targetSum):
            if root is None:
                return 
            targetSum -= root.val
            path.append(root.val)
            if not root.left and not root.right and targetSum == 0:
                res.append(path.copy())
            getRes(root.left, targetSum)
            getRes(root.right, targetSum)
            path.pop()
        getRes(root, targetSum)
        return res

            

5. Leetcode 129. 求根节点到叶节点数字之和 https://leetcode.cn/problems/sum-root-to-leaf-numbers/

解题思路:

我的笨比思路,通过前两道题我学会了如何获取从头节点到叶子节点的列表。 所以这道题我的做法就是拿到从头节点到叶子节点所有路径,然后全部变成字符串,再转换成数字,然后求和。

以下是代码

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution:
    def sumNumbers(self, root: Optional[TreeNode]) -> int:
        res, path = [], []
        def getRes(root):
            if root is None:
                return 
            path.append(str(root.val))
            if not root.left and not root.right:
                res.append(path.copy())
            getRes(root.left)
            getRes(root.right)
            path.pop()
        getRes(root)
        s = 0
        for i in res:
            p = ''.join(i)
            s += int(p)
        return s
        

6. Leetcode 257. 二叉树的所有路径 https://leetcode.cn/problems/binary-tree-paths/

解题思路:

easy题直接秒0 0, 直接复制上一题的代码稍作修改即可。

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution:
    def binaryTreePaths(self, root: Optional[TreeNode]) -> List[str]:
        res, path = [], []
        def getRes(root):
            if root is None:
                return 
            path.append(str(root.val))
            if not root.left and not root.right:
                res.append('->'.join(path.copy()))
            getRes(root.left)
            getRes(root.right)
            path.pop()
        getRes(root)
        return res
        

7. Leetcode 1448. 统计二叉树中好节点的数目 https://leetcode.cn/problems/count-good-nodes-in-binary-tree/

解题思路:

这几题真是顺下来的,灵神伟大,无需多言。 越做思路越清晰!

定义path列表,用来存放经过的全部节点。 

if 当前节点的值 >= path 列表中的所有值,那么!cnt += 1

以下是代码!

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution:
    def goodNodes(self, root: TreeNode) -> int:
        if root is None:
            return 0
        path = []
        cnt = 0
        def func(root):
            nonlocal cnt 
            if root is None:
                return 
            path.append(root.val)
            if all(root.val >= x for x in path):
                cnt += 1 
            func(root.left)
            func(root.right)
            path.pop()
        func(root)
        return cnt 
        

8. Leetcode987. 二叉树的垂序遍历 https://leetcode.cn/problems/vertical-order-traversal-of-a-binary-tree/

给你二叉树的根结点 root ,请你设计算法计算二叉树的 垂序遍历 序列。

对位于 (row, col) 的每个结点而言,其左右子结点分别位于 (row + 1, col - 1) 和 (row + 1, col + 1) 。树的根结点位于 (0, 0) 。

二叉树的 垂序遍历 从最左边的列开始直到最右边的列结束,按列索引每一列上的所有结点,形成一个按出现位置从上到下排序的有序列表。如果同行同列上有多个结点,则按结点的值从小到大进行排序。

返回二叉树的 垂序遍历 序列。

解题思路:

思路是有的!这道题要求按列遍历二叉树,那么思路就是用字典来保存col:[]。

即遍历节点的时候将相同列的值存放到一起。 此外还要求从上到下遍历,这意味着我们也要把行的信息保存下来。

[(3,2), [0,1],(0,-2),(1,4),(2,5)] 假设我们存在这样的列

首先要按行排序,当存在同行同列的元素时再按val排序。

补充知识

defaultdict

defaultdict是Python内建dict类的一个子类,第一个参数为default_factory属性提供初始值,默认为None。它覆盖一个方法并添加一个可写实例变量。它的其他功能与dict相同,但会为一个不存在的键提供默认值,从而避免KeyError异常。

以下是代码

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution:
    def verticalTraversal(self, root: Optional[TreeNode]) -> List[List[int]]:
        if root is None:
            return
        col, row = 0, 0
        dic = defaultdict(list)

        def func(root, row, col):
            if root is None:
                return
            dic[col].append((row, root.val))
            func(root.left, row + 1, col - 1)
            func(root.right, row + 1, col + 1)

        func(root, row, col)
        ans = []

        # 按键排序 也就是按列排序
        for _, z in sorted(dic.items()):
            # 按row排序,row相同则按val排序
            z.sort()
            ans.append([x for _, x in z])
        return ans

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值