今天主要是复习一下二叉树的相关内容,因为最近刷二叉树的相关题目比较迷茫。
- 二叉树算法题目多用递归法,递归法最重要的就是三步:确定参数和返回值、确定终止条件、写单层递归逻辑
- 今天做的题目包含的二叉树的模式有:
-
返回值型:最大深度、平衡二叉树、直径
-
双节点递归:相同的树、对称二叉树
-
携带状态型:所有路径
-
递归 + 回溯:
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 |