Day17 | leetcode110. 平衡二叉树、leetcode257. 二叉树的所有路径、leetcode404. 左叶子之和

Day17

  • leetcode110. 平衡二叉树
  • leetcode257. 二叉树的所有路径
  • leetcode404. 左叶子之和

leetcode110. 平衡二叉树

再强调一波概念:
二叉树节点的深度:指从根节点到该节点的最长简单路径边的条数。
二叉树节点的高度:指从该节点到叶子节点的最长简单路径边的条数。
Day17 | leetcode110. 平衡二叉树、leetcode257. 二叉树的所有路径、leetcode404. 左叶子之和_第1张图片

深度可以从上到下去查 所以需要前序遍历(中左右)
高度只能从下到上去查,所以只能后序遍历(左右中)

为什么104.二叉树的最大深度中求的是二叉树的最大深度,也用的是后序遍历。
那是因为代码的逻辑其实是求的根节点的高度,而根节点的高度就是这棵树的最大深度,所以才可以使用后序遍历。

而该题是要求比较高度的,所以必然是要后序遍历。
递归三步曲分析:

  1. 明确递归函数的参数和返回值
    参数:当前传入节点。 返回值:以当前传入节点为根节点的树的高度。
    那么如何标记左右子树是否差值大于1呢?
    如果当前传入节点为根节点的二叉树已经不是二叉平衡树了,还返回高度的话就没有意义了。
    所以如果已经不是二叉平衡树了,可以返回-1 来标记已经不符合平衡树的规则了。
  2. 明确终止条件
    递归的过程中依然是遇到空节点了为终止,返回0,表示当前节点为根节点的树高度为0
  3. 明确单层递归的逻辑
    如何判断以当前传入节点为根节点的二叉树是否是平衡二叉树呢?当然是其左子树高度和其右子树高度的差值。
    分别求出其左右子树的高度,然后如果差值小于等于1,则返回当前二叉树的高度,否则返回 -1,表示已经不是二叉平衡树了。

总结
通过本题可以了解求二叉树深度和二叉树高度的差异:求深度适合用前序遍历,而求高度适合用后序遍历。

class Solution:
    def isBalanced(self, root: Optional[TreeNode]) -> bool:
        return self.getHeight(root) != -1
    #
    def getHeight(self, node):
        if not node:
            return 0
        # 后序遍历求高度
        leftHeight = self.getHeight(node.left)
        rightHeight = self.getHeight(node.right)
        # 逻辑判断部分(-1的用法)
        if leftHeight == -1 or rightHeight == -1 or abs(leftHeight - rightHeight) > 1:
            return -1
        return 1 + max(leftHeight, rightHeight)

leetcode257. 二叉树的所有路径

这道题目要求从根节点到叶子的路径,所以需要前序遍历,这样才方便让父节点指向孩子节点,找到对应的路径。
在这道题目中将第一次涉及到回溯,因为我们要把路径记录下来,需要回溯来回退一个路径再进入另一个路径。
前序遍历以及回溯的过程如图:
Day17 | leetcode110. 平衡二叉树、leetcode257. 二叉树的所有路径、leetcode404. 左叶子之和_第2张图片
回溯和递归是一一对应的,有一个递归,就要有一个回溯。
此题建议在本地IDE上debug理解,看看如何递归和回溯的,如下:

from typing import List
# 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

# 构建二叉树(利用递归)
def build_binary_tree(nums):
    # 如果给定的列表 nums 是空列表,则返回 None
    if not nums:
        return None
    # 使用递归方式构建二叉树
    def helper(index):
        if index >= len(nums) or nums[index] is None:
            return None
        node = TreeNode(nums[index])
        node.left = helper(2 * index + 1)
        node.right = helper(2 * index + 2)
        return node
    root = helper(0)
    return root

# 测试代码(层序遍历, 每一层都得写上,即便为空. 比如深度为3的树就得填7个元素)
#nums = [1, 2, 3, 4, None, None, 7]
nums = [1,2,3,None,5,None,None]
root = build_binary_tree(nums)


def binaryTreePaths(root: TreeNode) -> List[str]:
    result = []
    path = []
    if not root:
        return result
    traversal(root, path, result)
    return result

def traversal(cur, path, result):
    # 中,前序遍历。(中为什么写在这里,因为最后一个节点也要加入到path中)
    path.append(cur.val)
    # 如果到达叶子节点
    if not cur.left and not cur.right:
        sPath = '->'.join(map(str, path))
        result.append(sPath)
        return
    # 左节点
    if cur.left:
        traversal(cur.left, path, result)
        path.pop()  # 回溯
    # 右节点
    if cur.right:
        traversal(cur.right, path, result)
        path.pop()  # 回溯

print(binaryTreePaths(root))

leetcode404. 左叶子之和

这题思路,首先要注意是判断左叶子,不是二叉树左侧节点,所以不要上来想着层序遍历。
左叶子的明确定义:节点A的左孩子不为空,且左孩子的左右孩子都为空(说明是叶子节点),那么A节点的左孩子为左叶子节点。
Day17 | leetcode110. 平衡二叉树、leetcode257. 二叉树的所有路径、leetcode404. 左叶子之和_第3张图片
判断当前节点是不是左叶子是无法判断的,必须要通过节点的父节点来判断其左孩子是不是左叶子。
如果该节点的左节点不为空,该节点的左节点的左节点为空,该节点的左节点的右节点为空,则找到了一个左叶子,判断代码如下:

if root.left and not root.left.left and not root.left.right:
	左叶子节点处理逻辑

总结:
平时我们解二叉树的题目时,已经习惯了通过节点的左右孩子判断本节点的属性,而本题我们要通过节点的父节点判断本节点的属性。

class Solution:
    def sumOfLeftLeaves(self, root: Optional[TreeNode]) -> int:
        # 排除没有左叶子的情况
        if not root:
            return 0
        if not root.left and not root.right:
            return 0
        # 左
        leftValue = self.sumOfLeftLeaves(root.left)
        # 左子树是左叶子的情况
        if root.left and not root.left.left and not root.left.right:
            leftValue = root.left.val
        # 右
        rightValue = self.sumOfLeftLeaves(root.right)
        # 中
        sumValue = leftValue + rightValue
        return sumValue

你可能感兴趣的:(算法编程题,算法,数据结构)