代码随想录刷题Day21

7 阅读4分钟

今天主要是复习一下二叉树的相关内容,因为最近刷二叉树的相关题目比较迷茫。

  1. 二叉树算法题目多用递归法,递归法最重要的就是三步:确定参数和返回值、确定终止条件、写单层递归逻辑
  2. 今天做的题目包含的二叉树的模式有:
  • 返回值型:最大深度、平衡二叉树、直径

  • 双节点递归:相同的树、对称二叉树

  • 携带状态型:所有路径

  • 递归 + 回溯:path.append() / path.pop()

相同的树

class Solution:
    def isSameTree(self, p: TreeNode | None, q: TreeNode | None) -> bool:
        if not p and not q:
            return True
        if not p and q:
            return False
        if not q and p:
            return False
        cur=p.val==q.val
        left=self.isSameTree(p.left,q.left)
        right=self.isSameTree(p.right,q.right)
        return cur and left and right

这道题我是第一次做,就做出来了,就是按照上述的逻辑来做的。但是类似的题目:对称二叉树,我是二刷,但是也没做出来

对称二叉树

# 错误解法
class Solution:
    def isSymmetric(self, root: TreeNode | None) -> bool:
        if not root:
            return True
        cur=root.left.val==root.right.val
        inside=root.left.right.val==root.right.left
        outsinde=root.left.left==root.right.right
        return cur and inside and outside

我一直在想怎么写单层递归,但是感觉递归不起来。实际上是因为这道题的参数写错了。题目里给的外层函数的root误导了我,这里真正的输入参数应该是left,right,返回布尔值

class Solution:
    def isSymmetric(self, root: TreeNode | None) -> bool:
        def compare(left:TreeNode,right:TreeNode)->bool:
            if not left and not right:
                return True
            if not left and right:
                return False
            if not right and left:
                return False
            cur=left.val==right.val
            inside=compare(left.right,right.left)
            outside=compare(left.left,right.right)
            return cur and inside and outside
        return compare(root.left,root.right)

compare 函数的作用是比较两个子树是否是关于根节点镜像对称。输入参数是两个子树的节点,返回bool值。终止条件:当两个子树都为None,返回True。其中一个子树为None,返回Fasle。如果两个子树都不为None,就判断子树的节点值是否相等,两个子树的内侧和外侧是否也关于根节点镜像对称。都满足也返回True

当前相等 + 内侧镜像 + 外侧镜像 = 两棵子树镜像。

二叉树的所有路径

这道题就比较特殊,之前的题基本都是先判断左右孩子的信息,然后返回给父节点做判断。这个题是要带着父节点的信息往下传。而且这道题还带有回溯的思想。还有小的处理细节,所以这道题虽然leetcode上标注的是简单,但我感觉还是有难度的

class Solution:
    def binaryTreePaths(self, root: TreeNode | None) -> list[str]:
        result=[]
        def traversal(node:TreeNode,path=None):
            if path==None: # 可变对象不要直接作为默认参数
                path=[]
            path.append(node.val)
            if node.left==None and node.right==None:
                result.append('->'.join(map(str,path))) # join只能针对字符列表
            if node.left: # 路径中不能加入空节点
                traversal(node.left,path) # 递归节点,还要传入路径
                path.pop() # 回溯
            if node.right:
                traversal(node.right,path)
                path.pop()
            return result
        if root is None:
            return result
        return traversal(root)

这道题就是要带着父节点走过的路径往下传,所以输入参数里有path。

path.pop()就是回溯的过程->返回上面的节点。

平衡二叉树

class Solution:
    def isBalanced(self, root: TreeNode | None) -> bool:
        if root is None:
            return True
        def getheight(node:TreeNode)->int:
            if node is None:
                return 0
            leftheight=getheight(node.left)
            rightheight=getheight(node.right)
            if leftheight==-1 or rightheight==-1:
                return -1
            if abs(leftheight-rightheight)>1:
                return -1
            else:
                return max(leftheight,rightheight)+1
        return getheight(root)!=-1

这道题其实也是比较经典的后序遍历题目,就是需要左右孩子的信息,才能判断父节点。比较有意思的是这道题用-1作为不平衡的信号,一路传给父节点。只要最后getheight==-1,就说明这个树一定不是平衡二叉树

二叉树的直径

class Solution:
    def diameterOfBinaryTree(self, root: Optional[TreeNode]) -> int:
        result=0
        if root is None or (root.left is None and root.right is None):
            return 0
        def getheight(node:TreeNode)->int:
            nonlocal result
            if node is None:
                return 0
            left_height=getheight(node.left)
            right_height=getheight(node.right)

            tmp=left_height+right_height
            result=max(result,tmp)

            return max(left_height,right_height)+1
        getheight(root)
        return result

这道题又是在二叉树的高度上衍生的题目。之前那道平衡二叉树也是二叉树高度的衍生题目。平衡二叉树使用高度=-1作为不平衡的信号。这道题是额外计算一个经过当前节点的直径,然后一直维护一个最大直径。

让 GPT 给我生成了一个简单的总结:

题目更典型的思路为什么
104 最大深度后序左右孩子先返回高度,父节点 max(left,right)+1
111 最小深度后序左右孩子先返回最小深度,再处理单边为空
226 翻转二叉树前序/后序都可以交换左右孩子可以在递归前,也可以递归后
100 相同的树当前节点先判断,再递归先判断 p/q 是否为空、值是否相等,再比较孩子
101 对称二叉树当前节点先判断,再递归先判断当前两个节点,再递归比较内外侧
257 所有路径前序 + 回溯先把当前节点加入 path,然后往孩子走
110 平衡二叉树典型后序必须先拿到左右子树高度,才能判断当前节点
543 二叉树直径典型后序孩子返回高度,当前节点计算 left + right