226.翻转二叉树 (优先掌握递归)
class Solution:
def invertTree(self, root: Optional[TreeNode]) -> Optional[TreeNode]:
# 递归法:前序遍历:
# if not root:
# return None
# root.left,root.right=root.right,root.left
# self.invertTree(root.left)
# self.invertTree(root.right)
# return root
# # 迭代法:前序遍历:
# if not root:
# return None
# stack=[root]
# while stack:
# node=stack.pop()
# node.left,node.right=node.right,node.left
# if node.left:
# stack.append(node.left)
# if node.right:
# stack.append(node.right)
# return root
if not root:
return None
queue=collections.deque([root])
while queue:
for _ in range(len(queue)):
node=queue.popleft()
node.left,node.right=node.right,node.left
if node.left:
queue.append(node.left)
if node.right:
queue.append(node.right)
return root
101. 对称二叉树 (优先掌握递归)
思路:
- 判断对称二叉树要比较的是哪两个节点,要比较的可不是左右节点!
- 对于二叉树是否对称,要比较的是根节点的左子树与右子树是不是相互翻转的,理解这一点就知道了其实我们要比较的是两个树(这两个树是根节点的左右子树),所以在递归遍历的过程中,也是要同时遍历两棵树。
- 如何进行比较:比较的是两个子树的里侧和外侧的元素是否相等。如图所示:
- 遍历的顺序:本题遍历只能是“后序遍历”,因为我们**要通过递归函数的返回值来判断两个子树的内侧节点和外侧节点是否相等。**正是因为要遍历两棵树而且要比较内侧和外侧节点,所以准确的来说是一个树的遍历顺序是左右中,一个树的遍历顺序是右左中。
递归三部曲:
- 确定递归函数的参数和返回值:要比较的是根节点的两个子树是否是相互翻转的,进而判断这个树是不是对称树,所以要比较的是两个树,参数自然也是左子树节点和右子树节点。返回值自然是bool类型。
- 确定终止条件:要比较两个节点数值相不相同,首先要把两个节点为空的情况弄清楚!否则后面比较数值的时候就会操作空指针了。
- 节点为空的情况有:(注意比较的其实不是左孩子和右孩子,所以如下称之为左节点右节点)
- 此时已经排除掉了节点为空的情况,那么剩下的就是左右节点不为空:左右都不为空,比较节点值,不相同就return false。此时左右节点不为空,且数值也不相同的情况我们也处理了。
- 确定单层递归的逻辑:此时才进入单层递归的逻辑,单层递归的逻辑就是处理 左右节点都不为空,且数值相同的情况。
- 比较二叉树外侧是否对称:传入的是左节点的左孩子,右节点的右孩子。
- 比较内侧是否对称,传入左节点的右孩子,右节点的左孩子。
- 如果左右都对称就返回true ,有一侧不对称就返回false 。
class Solution:
def compare(self,left,right):
# 首先解决return False的情况
if left==None and right !=None:return False
elif left!=None and right ==None:return False
elif left==None and right==None:return True
elif left.val!=right.val:return False
# 解决return True的情况
#此时就是:左右节点都不为空,且数值相同的情况
#此时才做递归,做下一层的判断
outside=self.compare(left.left,right.right)#左子树:左、 右子树:右
inside=self.compare(left.right,right.left) #左子树:右、 右子树:左
return outside and inside# 两者都为true时才可以
def isSymmetric(self, root: Optional[TreeNode]) -> bool:
# 1. 递归法
if not root:
return True
return self.compare(root.left,root.right)
注意left==right和left and right的区别,前者比较两个值是否相等,结果是一个布尔值;left and right取决于两者的值。
104.二叉树的最大深度 (优先掌握递归)
- 什么是深度,什么是高度,如何求深度,如何求高度,这里有关系到二叉树的遍历方式。
- 二叉树的深度为根节点到最远叶子节点的最长路径上的节点数。叶子节点是指没有子节点的节点。
- 本题可以使用前序(中左右),也可以使用后序遍历(左右中),使用前序求的就是深度,使用后序求的是高度。而根节点的高度就是二叉树的最大深度,所以本题中我们通过后序求的根节点高度来求的二叉树最大深度。
- 二叉树节点的深度:指从根节点到该节点的最长简单路径边的条数或者节点数(取决于深度从0开始还是从1开始)
- 二叉树节点的高度:指从该节点到叶子节点的最长简单路径边的条数或者节点数(取决于高度从0开始还是从1开始)
- 先用后序遍历(左右中)来计算树的高度。递归三部曲:
1. 确定递归函数的参数和返回值:参数就是传入树的根节点,返回就返回这棵树的深度,所以返回值为int类型。
2. 确定终止条件:如果为空节点的话,就返回0,表示高度为0。
3. 确定单层递归的逻辑:先求它的左子树的深度,再求右子树的深度,最后取左右深度最大的数值 再+1 (加1是因为算上当前中间节点)就是目前节点为根节点的树的深度。
后序遍历:
class Solution:
def getdepth(self,node):
if not node:
return 0
leftheight=self.getdepth(node.left)
rightheight=self.getdepth(node.right)
height=1+max(leftheight,rightheight)
return height
def maxDepth(self, root: Optional[TreeNode]) -> int:
return self.getdepth(root)
层序遍历:
def maxDepth(self, root: Optional[TreeNode]) -> int:
# 使用层序遍历
if not root:
return 0
que=collections.deque([root])
res=[]
while que:
level=[]
for _ in range(len(que)):
cur=que.popleft()
level.append(cur.val)
if cur.left:
que.append(cur.left)
if cur.right:
que.append(cur.right)
res.append(level)
return len(res)
111.二叉树的最小深度 (优先掌握递归)
- 和最大深度看似差不多,其实差距还挺大,有坑。
- 依然是前序遍历和后序遍历都可以,前序求的是深度,后序求的是高度。
- 二叉树节点的深度:指从根节点到该节点的最长简单路径边的条数或者节点数(取决于深度从0开始还是从1开始)
- 二叉树节点的高度:指从该节点到叶子节点的最长简单路径边的条数后者节点数(取决于高度从0开始还是从1开始)
- 使用后序遍历,其实求的是根节点到叶子节点的最小距离,就是求高度的过程,不过这个最小距离也同样是最小深度。
- 最大深度很容易理解,最小深度就不那么好理解,如图:
- 最小深度是从根节点到最近叶子节点的最短路径上的节点数量。注意是叶子节点。左右孩子都为空的节点才是叶子节点!
递归三部曲:
- 确定递归函数的参数和返回值:参数为要传入的二叉树根节点,返回的是int类型的深度。
- 确定终止条件:终止条件也是遇到空节点返回0,表示当前节点的高度为0。
- 确定单层递归的逻辑:这块和求最大深度可就不一样了,注意下面错误,这么求的话,没有左孩子的分支会算为最短深度。
- 所以,如果左子树为空,右子树不为空,说明最小深度是 1 + 右子树的深度。
- 右子树为空,左子树不为空,最小深度是 1 + 左子树的深度。
- 如果左右子树都不为空,返回左右子树深度最小值 + 1 。
class Solution:
def getmindepth(self,node):
if not node:
return 0
# 最小深度:根节点到叶子结点的最小距离
leftdepth=self.getmindepth(node.left)
rightdepth=self.getmindepth(node.right)
# 如果其中一个子树为空,返回不为空的子树的深度 + 1
if not node.left or not node.right:
return 1 + leftdepth + rightdepth
# 如果两个子树都不为空,返回较小深度 + 1
return 1 + min(leftdepth, rightdepth)
层序遍历:只有当左右孩子都为空的时候,才说明遍历到最低点了。如果其中一个孩子不为空则不是最低点
def minDepth(self, root: Optional[TreeNode]) -> int:
使用层序遍历
if not root:
return 0
que=collections.deque([root])
depth=0
while que:
depth+=1
for _ in range(len(que)):# 当子结点都为空时,才为叶子结点
cur=que.popleft()
if not cur.left and not cur.right:
return depth
if cur.left:
que.append(cur.left)
if cur.right:
que.append(cur.right)