代码随想录算法训练营Day12 | Leetcode 226翻转二叉树、101对称二叉树、104二叉树的最大深度、111二叉树的最小深度
代码随想录算法训练营Day12 | Leetcode 226翻转二叉树、101对称二叉树、104二叉树的最大深度、111二叉树的最小深度
一、翻转二叉树
相关题目:Leetcode226
文档讲解:Leetcode226
视频讲解:Leetcode226
1. Leetcode226.翻转二叉树
给你一棵二叉树的根节点 root ,翻转这棵二叉树,并返回其根节点。
- 思路:
- 翻转二叉树其实就相当于就把每一个节点的左右子节点交换。
- 递归法:
- 确定递归函数的参数和返回值:参数就是要传入节点的指针,不需要其他参数了。
- 确定终止条件:当前节点为空的时候,就返回。
- 确定单层递归的逻辑:若是前序遍历,则先进行交换左右子节点,然后反转左子树,反转右子树。
- 迭代法:
- 深度优先遍历,与二叉树的迭代遍历相同,也可采用二叉树的统一迭代法。
- 广度优先遍历,也就是层序遍历,层序遍历可以把每个节点的左右孩子都翻转一遍。
- 翻转二叉树其实就相当于就把每一个节点的左右子节点交换。
- 递归法
#前序遍历:
# 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
class Solution:
def invertTree(self, root: TreeNode) -> TreeNode:
if not root:
return None
root.left, root.right = root.right, root.left
self.invertTree(root.left)
self.invertTree(root.right)
return root
#中序遍历:
class Solution:
def invertTree(self, root: TreeNode) -> TreeNode:
if not root:
return None
self.invertTree(root.left)
root.left, root.right = root.right, root.left
self.invertTree(root.left)
return root
#后序遍历:
class Solution:
def invertTree(self, root: TreeNode) -> TreeNode:
if not root:
return None
self.invertTree(root.left)
self.invertTree(root.right)
root.left, root.right = root.right, root.left
return root
- 迭代法
#前序遍历:
# 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
class Solution:
def invertTree(self, root: TreeNode) -> TreeNode:
if not root:
return None
stack = [root]
while stack:
node = stack.pop()
node.left, node.right = node.right, node.left
if node.right:
stack.append(node.right)
if node.left:
stack.append(node.left)
return root
#伪中序遍历(结果是对的,看起来像是中序遍历,实际上它是前序遍历,只不过把中间节点处理逻辑放到了中间。还是要用'统一写法'才是真正的中序遍历):
class Solution:
def invertTree(self, root: TreeNode) -> TreeNode:
if not root:
return None
stack = [root]
while stack:
node = stack.pop()
if node.right:
stack.append(node.right)
node.left, node.right = node.right, node.left # 放到中间,依然是前序遍历
if node.right:
stack.append(node.right)
return root
#伪后序遍历(结果是对的,看起来像是后序遍历,实际上它是前序遍历,只不过把中间节点处理逻辑放到了最后。还是要用'统一写法'才是真正的后序遍历):
class Solution:
def invertTree(self, root: TreeNode) -> TreeNode:
if not root:
return None
stack = [root]
while stack:
node = stack.pop()
if node.right:
stack.append(node.right)
if node.left:
stack.append(node.left)
node.left, node.right = node.right, node.left
return root
- 广度优先遍历
#广度优先遍历(层序遍历):
# 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
class Solution:
def invertTree(self, root: TreeNode) -> TreeNode:
if not root:
return None
queue = collections.deque([root])
while 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
二、对称二叉树
相关题目:Leetcode101、Leetcode100、Leetcode572
文档讲解:Leetcode101
视频讲解:Leetcode101
1. Leetcode101.对称二叉树
给你一个二叉树的根节点 root , 检查它是否轴对称。
- 思路:
- 对于二叉树是否对称,要比较的是根节点的左子树与右子树是不是相互翻转的,其实要比较的是两个树(这两个树是根节点的左右子树),所以在递归遍历的过程中,也是要同时遍历两棵树。
- 本题遍历只能是“后序遍历”,因为要通过递归函数的返回值来判断两个子树的内侧节点和外侧节点是否相等。正是因为要遍历两棵树而且要比较内侧和外侧节点,所以准确的来说是一个树的遍历顺序是左右中,一个树的遍历顺序是右左中。
- 递归法:
-
确定递归函数的参数和返回值:要比较的是根节点的两个子树是否是相互翻转的,进而判断这个树是不是对称树,所以要比较的是两个树,参数自然也是左子树节点和右子树节点。返回值自然是 bool 类型。
-
确定终止条件:首先要考虑两个节点为空的情况,节点为空的情况有:
- 左节点为空,右节点不为空,不对称,return false。
- 左不为空,右为空,不对称 return false。
- 左右都为空,对称,返回 true。
此时已经排除掉了节点为空的情况,那么剩下的就是左右节点不为空:
- 左右都不为空,比较节点数值,不相同就 return false。
-
确定单层递归的逻辑:处理 左右节点都不为空,且数值相同的情况:
- 比较二叉树外侧是否对称:传入的是左节点的左孩子,右节点的右孩子。
- 比较内侧是否对称,传入左节点的右孩子,右节点的左孩子。
- 如果左右都对称就返回 true ,有一侧不对称就返回 false 。
-
- 迭代法:
- 本题的本质是判断两个树是否是相互翻转的,其实已经不是所谓二叉树遍历的前中后序的关系了。可以使用队列来比较两个树(根节点的左右子树)是否相互翻转。(注意这不是层序遍历)
- 本题的本质是判断两个树是否是相互翻转的,其实已经不是所谓二叉树遍历的前中后序的关系了。可以使用队列来比较两个树(根节点的左右子树)是否相互翻转。(注意这不是层序遍历)
- 对于二叉树是否对称,要比较的是根节点的左子树与右子树是不是相互翻转的,其实要比较的是两个树(这两个树是根节点的左右子树),所以在递归遍历的过程中,也是要同时遍历两棵树。
- 递归法
class Solution:
def isSymmetric(self, root: TreeNode) -> bool:
if not root:
return True
return self.compare(root.left, root.right)
def compare(self, left, right):
#首先排除空节点的情况
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
#此时就是:左右节点都不为空,且数值相同的情况
#此时才做递归,做下一层的判断
outside = self.compare(left.left, right.right) #左子树:左、 右子树:右
inside = self.compare(left.right, right.left) #左子树:右、 右子树:左
isSame = outside and inside #左子树:中、 右子树:中 (逻辑处理)
return isSame
- 迭代法
#使用队列
import collections
class Solution:
def isSymmetric(self, root: TreeNode) -> bool:
if not root:
return True
queue = collections.deque()
queue.append(root.left) #将左子树头结点加入队列
queue.append(root.right) #将右子树头结点加入队列
while queue: #接下来就要判断这这两个树是否相互翻转
leftNode = queue.popleft()
rightNode = queue.popleft()
if not leftNode and not rightNode: #左节点为空、右节点为空,此时说明是对称的
continue
#左右一个节点不为空,或者都不为空但数值不相同,返回false
if not leftNode or not rightNode or leftNode.val != rightNode.val:
return False
queue.append(leftNode.left) #加入左节点左孩子
queue.append(rightNode.right) #加入右节点右孩子
queue.append(leftNode.right) #加入左节点右孩子
queue.append(rightNode.left) #加入右节点左孩子
return True
#使用栈
class Solution:
def isSymmetric(self, root: TreeNode) -> bool:
if not root:
return True
st = [] #这里改成了栈
st.append(root.left)
st.append(root.right)
while st:
rightNode = st.pop()
leftNode = st.pop()
if not leftNode and not rightNode:
continue
if not leftNode or not rightNode or leftNode.val != rightNode.val:
return False
st.append(leftNode.left)
st.append(rightNode.right)
st.append(leftNode.right)
st.append(rightNode.left)
return True
三、二叉树的最大深度
相关题目:Leetcode104、Leetcode559
文档讲解:Leetcode104
视频讲解:Leetcode104
1. Leetcode104.二叉树的最大深度
给定一个二叉树 root ,返回其最大深度。二叉树的 最大深度 是指从根节点到最远叶子节点的最长路径上的节点数。
-
思路:
- 本题可以使用前序(中左右),也可以使用后序遍历(左右中),使用前序求的就是深度,使用后序求的是高度。根节点的高度就是二叉树的最大深度。
- 二叉树节点的深度:指从根节点到该节点的最长简单路径边的条数或者节点数(取决于深度从0开始还是从1开始)
- 二叉树节点的高度:指从该节点到叶子节点的最长简单路径边的条数或者节点数(取决于高度从0开始还是从1开始)
- 递归法(后序遍历):
- 确定递归函数的参数和返回值:参数就是传入树的根节点,返回就返回这棵树的深度,所以返回值为int类型。
- 确定终止条件:如果为空节点的话,就返回 0,表示高度为 0。
- 确定单层递归的逻辑:先求它的左子树的深度,再求右子树的深度,最后取左右深度最大的数值再+1 (加1是因为算上当前中间节点)就是目前节点为根节点的树的深度。
- 迭代法:
使用层序遍历是最为合适的,因为最大的深度就是二叉树的层数,和层序遍历的方式极其吻合。
- 本题可以使用前序(中左右),也可以使用后序遍历(左右中),使用前序求的就是深度,使用后序求的是高度。根节点的高度就是二叉树的最大深度。
-
递归法
##104.二叉树的最大深度
class Solution:
def maxdepth(self, root: treenode) -> int:
return self.getdepth(root)
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
#精简代码
class Solution:
def maxdepth(self, root: treenode) -> int:
if not root:
return 0
return 1 + max(self.maxdepth(root.left), self.maxdepth(root.right))
##559.n叉树的最大深度
class Solution:
def maxDepth(self, root: 'Node') -> int:
if not root:
return 0
max_depth = 1
for child in root.children:
max_depth = max(max_depth, self.maxDepth(child) + 1)
return max_depth
- 层序遍历
##104.二叉树的最大深度
class Solution:
def maxDepth(self, root: TreeNode) -> int:
if not root:
return 0
depth = 0
queue = collections.deque([root])
while queue:
depth += 1
for _ in range(len(queue)):
node = queue.popleft()
if node.left:
queue.append(node.left)
if node.right:
queue.append(node.right)
return depth
##559.n叉树的最大深度
class Solution:
def maxDepth(self, root: TreeNode) -> int:
if not root:
return 0
depth = 0
queue = collections.deque([root])
while queue:
depth += 1
for _ in range(len(queue)):
node = queue.popleft()
for child in node.children:
queue.append(child)
return depth
四、二叉树的最小深度
相关题目:Leetcode111
文档讲解:Leetcode111
视频讲解:Leetcode111
1. Leetcode111.二叉树的最小深度
给定一个二叉树,找出其最小深度。最小深度 是从根节点到最近叶子节点的最短路径上的节点数量。
说明:叶子节点是指没有子节点的节点。
-
思路:
- 左右孩子都为空的节点才是叶子节点:
- 递归法:
- 确定递归函数的参数和返回值:参数为要传入的二叉树根节点,返回的是int类型的深度。
- 确定终止条件:终止条件也是遇到空节点返回 0,表示当前节点的高度为 0。
- 确定单层递归的逻辑:
- 如果左子树为空,右子树不为空,说明最小深度是 1 + 右子树的深度。
- 如果右子树为空,左子树不为空,最小深度是 1 + 左子树的深度。
- 如果左右子树都不为空,返回左右子树深度最小值 + 1 。
- 迭代法:
可以使用层序遍历,需要注意的是,只有当左右孩子都为空的时候,才说明遍历到最低点了。如果其中一个孩子不为空则不是最低点。
- 左右孩子都为空的节点才是叶子节点:
-
注意:
- 在递归法的单层递归逻辑中,如果直接像 Leetcode104.二叉树的最大深度 那样返回左右子树深度最小值 + 1 ,则前述图中最小深度会计算为 1,因为根节点的左子树为空,直接返回最小深度。
-
递归法
class Solution:
def getDepth(self, node):
if node is None:
return 0
leftDepth = self.getDepth(node.left) # 左
rightDepth = self.getDepth(node.right) # 右
# 当一个左子树为空,右不为空,这时并不是最低点
if node.left is None and node.right is not None:
return 1 + rightDepth
# 当一个右子树为空,左不为空,这时并不是最低点
if node.left is not None and node.right is None:
return 1 + leftDepth
result = 1 + min(leftDepth, rightDepth)
return result
def minDepth(self, root):
return self.getDepth(root)
#精简
class Solution:
def minDepth(self, root):
if root is None:
return 0
if root.left is None and root.right is not None:
return 1 + self.minDepth(root.right)
if root.left is not None and root.right is None:
return 1 + self.minDepth(root.left)
return 1 + min(self.minDepth(root.left), self.minDepth(root.right))
- 迭代法
# 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
class Solution:
def minDepth(self, root: TreeNode) -> int:
if not root:
return 0
depth = 0
queue = collections.deque([root])
while queue:
depth += 1
for _ in range(len(queue)):
node = queue.popleft()
if not node.left and not node.right:
return depth
if node.left:
queue.append(node.left)
if node.right:
queue.append(node.right)
return depth