调整好状态,重新出发!
平衡二叉树
class Solution:
def isBalanced(self, node: TreeNode | None) -> bool:
if not node:
return True
def getheight(node:TreeNode)->int:
if not node:
return 0
return max(getheight(node.left),getheight(node.right))+1
left_balanced=self.isBalanced(node.left)
right_balanced=self.isBalanced(node.right)
# 需要检查自己的左右子树高度差
left_right=abs(getheight(node.left)-getheight(node.right))
return left_balanced and right_balanced and (left_right<=1)
判断以node为节点的树是否为平衡二叉树需要满足:
- 左右子树都是平衡二叉树
- 自己的左右子树高度差的绝对值小于等于1
这道题还有一个比较巧妙的解法,核心思路就是:
后序遍历一边计算高度,一边检查平衡;正常就向上返回高度,异常就向上返回
-1
class Solution:
def isBalanced(self, node: TreeNode | None) -> bool:
if not node:
return True
def getheight(node:TreeNode)->int:
if not node:
return 0
left_height=getheight(node.left)
right_height=getheight(node.right)
if abs(left_height-right_height)>1:
return -1
if left_height==-1 or right_height==-1:
return -1
else:
return max(left_height,right_height)+1
return getheight(node)!=-1
平衡二叉树要求每个节点的左右子树高度差的绝对值不超过 1。由于判断平衡本身就需要知道左右子树的高度,因此可以在 getheight 递归计算高度的过程中顺便判断当前节点是否平衡。对于每个节点,先递归获得左右子树的高度;如果某个子树已经返回 -1,说明该子树内部已经不平衡,则当前节点也继续返回 -1。如果左右子树都正常,再判断它们的高度差是否超过 1,若超过则返回 -1 作为“不平衡”的故障信号;否则返回当前节点的正常高度 max(left, right) + 1。这样一旦某处出现不平衡,-1 就会沿递归调用逐层向上传递到根节点。
二叉树的所有路径
# 力扣不通过
class Solution:
def binaryTreePaths(self, node: TreeNode, path=[], result=[]):
path.append(node.val)
if not node.left and not node.right:
result.append('->'.join(map(str,path)))
if node.left:
self.binaryTreePaths(node.left,path,result)
path.pop()
if node.right:
self.binaryTreePaths(node.right,path,result)
path.pop()
return result
这个逻辑是没有问题的。
- 递归函数的参数是节点,路径,结果
- 终止条件是是叶子节点,将这条路径添加到结果里
- 启动递归,前序遍历(中添加数值到path,递归左+回溯,递归右+回溯)
class Solution:
def binaryTreePaths(self, node: TreeNode, path=None, result=None):
if path is None: #不能写not path,因为包含了列表为空的情况
path=[]
if result is None:
result=[]
path.append(node.val)
if not node.left and not node.right:
result.append('->'.join(map(str,path)))
if node.left:
self.binaryTreePaths(node.left,path,result)
path.pop()
if node.right:
self.binaryTreePaths(node.right,path,result)
path.pop()
return result
问题出现在python的特性上 比如:
def f(a=[]):
a.append(1)
print(a)
第一次调用:
f()
因为你没传 a,Python 会自动使用默认值 []。
问题在于:这个 [] 是函数定义时就创建好的同一个列表。
所以第一次:
默认列表原来是 []
append(1)
变成 [1]
第二次再:
f()
Python 确实又“自动使用默认参数”,但它用的是同一个默认列表,现在它已经是:
[1]
所以再 append:
[1, 1]
因此:
f()
f()
f()
输出:
[1]
[1, 1]
[1, 1, 1]
你可以把它理解成:
# 大概类似于 Python 在函数定义时偷偷做了
默认a = []
def f():
a = 默认a
每次 f() 没传参数时,都是去拿这个 默认a,而不是重新执行一次:
a = []
所以一句话记:
默认参数会在没传参时自动使用,但默认对象本身不会每次重新创建。
这也是为什么通常写:
def f(a=None):
if a is None:
a = []
因为这里的:
a = []
是在每次调用函数时真正执行的,所以每次都会创建新列表。
左叶子之和
class Solution:
def sumOfLeftLeaves(self, node: Optional[TreeNode]) -> int:
if node==None:
return 0
leftvalue=self.sumOfLeftLeaves(node.left)
if node.left and not node.left.left and not node.left.right:
leftvalue=node.left.val
rightvalue=self.sumOfLeftLeaves(node.right)
result= leftvalue + rightvalue
return result
这道题的话首先需要知道什么是左叶子。如果节点的左孩子是叶子节点,那么这个左孩子就是左叶子
然后就是后序遍历的处理逻辑了。
我发现二叉树的题,做到目前为止大致两种:
- 需要遍历所有的节点,前序遍历+处理
- 需要左右孩子的结果汇总到父节点,后续遍历+处理
就是所谓的递归,基本都是建立在遍历上的。
完全二叉树的节点个数
常规解法
用后序遍历,遍历所有的节点
class Solution:
def countNodes(self, root: Optional[TreeNode]) -> int:
if not root:
return 0
leftcount=self.countNodes(root.left)
rightcount=self.countNodes(root.right)
total_count=leftcount+rightcount+1
return total_count
可以实现功能,但是力扣没法通过,是需要有简化的方法的。
利用完全二叉树和满二叉树的关系
在完全二叉树中,如果递归向左遍历的深度等于递归向右遍历的深度,那说明就是满二叉树。
class Solution:
def countNodes(self, root: Optional[TreeNode]) -> int:
if not root:
return 0
left=root.left
right=root.right
left_depth=1
right_depth=1
while left:
left=left.left
left_depth+=1
while right:
right=right.right
right_depth+=1
if left_depth==right_depth:
return 2**left_depth-1
return self.countNodes(root.left)+self.countNodes(root.right)+1
这题到时候回来再做一下,每台搞懂。我现在需要把之前二叉树的题再复习一下,递归这一块还是比较抽象