今日任务:
1)层序遍历10题
2)226翻转二叉树
3)对称二叉树
层序遍历
题目链接:
107. 二叉树的层序遍历 II - 力扣(LeetCode)
637.二叉树的层平均值(opens new window)
429.N叉树的层序遍历(opens new window)
515.在每个树行中找最大值(opens new window)
116.填充每个节点的下一个右侧节点指针(opens new window)
117.填充每个节点的下一个右侧节点指针II(opens new window)
104.二叉树的最大深度(opens new window)
文章讲解:代码随想录 (programmercarl.com)
102. 二叉树的层序遍历
给你二叉树的根节点 root ,返回其节点值的 层序遍历 。 (即逐层地,从左到右访问所有节点)
示例 1:
输入:root = [3,9,20,null,null,15,7]
输出:[[3],[9,20],[15,7]]
class Solution:
def levelOrder(self, root: [TreeNode]) -> list[list[int]]:
if not root:
return []
queue = collections.deque([root])
result = []
while queue:
level = []
for _ in range(len(queue)):
cur = queue.popleft()
level.append(cur.val)
if cur.left:
queue.append(cur.left)
if cur.right:
queue.append(cur.right)
result.append(level)
return result
107. 二叉树的层序遍历 II
给定一个二叉树,返回其节点值自底向上的层次遍历。 (即按从叶子节点所在层到根节点所在的层,逐层从左向右遍历)
示例 1:
输入:root = [3,9,20,null,null,15,7]
输出:[[15,7],[9,20],[3]]
把遍历Ⅰ得到的结果反转即可
class Solution:
def levelOrderBottom(self, root: [TreeNode]) -> list[list[int]]:
if not root:
return []
queue = collections.deque([root])
result = []
while queue:
level = []
for _ in range(len(queue)):
cur = queue.popleft()
level.append(cur.val)
if cur.left:
queue.append(cur.left)
if cur.right:
queue.append(cur.right)
result.append(level)
return result[::-1]
637.二叉树的层平均值
题目:给定一个非空二叉树的根节点 root , 以数组的形式返回每一层节点的平均值。与实际答案相差 10-5 以内的答案可以被接受。
示例 1:
输入:
root = [3,9,20,null,null,15,7]
输出:[3.00000,14.50000,11.00000]
只需要在遍历完一层后去平均即可
class Solution:
def averageOfLevels(self, root: TreeNode) -> list[float]:
if not root:
return []
queue = collections.deque([root])
res = []
while queue:
level_num = 0
size = len(queue)
for _ in range(size):
cur = queue.popleft()
level_num += cur.val
if cur.left:
queue.append(cur.left)
if cur.right:
queue.append(cur.right)
res.append(level_num/size)
return res
429.N叉树的层序遍历
给定一个 N 叉树,返回其节点值的层序遍历。 (即从左到右,逐层遍历)。
与二叉树代码区别只是不再是左右节点,而变成孩子节点,其余不变
class Node:
def __init__(self, val=None, children=None):
self.val = val
self.children = children
class Solution:
def levelorder(self, root: Node) -> list[list[int]]:
if not root:
return []
queue = collections.deque([root])
result = []
while queue:
level = []
for _ in range(len(queue)):
cur = queue.popleft()
level.append(cur.val)
for child in cur.children:
queue.append(child)
result.append(level)
return result
515.在每个树行中找最大值
这题只需要在遍历完每层节点后,取max
class Solution:
def largestValues(self, root: [TreeNode]) -> list[int]:
if not root:
return []
queue = collections.deque([root])
result = []
while queue:
max_ = float('-inf')
for _ in range(len(queue)):
cur = queue.popleft()
if cur.left:
queue.append(cur.left)
if cur.right:
queue.append(cur.right)
max_=max(max_,cur.val)
result.append(max_)
return result
116.填充每个节点的下一个右侧节点指针
给定一个完美二叉树,其所有叶子节点都在同一层,每个父节点都有两个子节点。
填充它的每个 next 指针,让这个指针指向其下一个右侧节点。如果找不到下一个右侧节点,则将 next 指针设置为 NULL。
初始状态下,所有 next 指针都被设置为 NULL。
# Definition for a Node.
class Node:
def __init__(self, val: int = 0, left: 'Node' = None, right: 'Node' = None, next: 'Node' = None):
self.val = val
self.left = left
self.right = right
self.next = next
class Solution:
def connect(self, root: '[Node]') -> '[Node]':
if not root:
return root
queue = collections.deque([root])
while queue:
pref = None
for i in range(len(queue)):
cur = queue.popleft()
if pref:
pref.next = cur
pref = cur
if cur.left:
queue.append(cur.left)
if cur.right:
queue.append(cur.right)
return root
117.填充每个节点的下一个右侧节点指针II
与上一题完美二叉树一样的代码
class Node:
def __init__(self, val: int = 0, left: 'Node' = None, right: 'Node' = None, next: 'Node' = None):
self.val = val
self.left = left
self.right = right
self.next = next
class Solution:
def connect(self, root: 'Node') -> 'Node':
if not root:
return root
queue = collections.deque([root])
while queue:
pref = None
for i in range(len(queue)):
cur = queue.popleft()
if pref:
pref.next = cur
pref = cur
if cur.left:
queue.append(cur.left)
if cur.right:
queue.append(cur.right)
return root
104.二叉树的最大深度
给定一个二叉树 root ,返回其最大深度。 二叉树的 最大深度 是指从根节点到最远叶子节点的最长路径上的节点数。
用层序遍历的话,每遍历一层深度加1,直到结束即可
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
class Solution:
def maxDepth(self, root: [TreeNode]) -> int:
if not root:
return 0
queue = collections.deque([root])
deepmax = 0
while queue:
deepmax += 1
for _ in range(len(queue)):
cur = queue.popleft()
if cur.left:
queue.append(cur.left)
if cur.right:
queue.append(cur.right)
return deepmax
111.二叉树的最小深度
给定一个二叉树,找出其最小深度。 最小深度是从根节点到最近叶子节点的最短路径上的节点数量。
遇上一题相似,还是采用层序遍历,每遍历一层深度加1,当遇见某一个节点左孩子与右孩子均为空则跳出
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
queue = collections.deque([root])
minmax = 0
while queue:
minmax += 1
for _ in range(len(queue)):
cur = queue.popleft()
if not cur.left and not cur.right:
return minmax
if cur.left:
queue.append(cur.left)
if cur.right:
queue.append(cur.right)
return minmax
感想:昨天已经详细讲了层序遍历,这几题基本是套模板,今天就不再重复了,每一题在构建层序遍历上稍有改动
226翻转二叉树 (优先掌握递归)
题目链接:226. 翻转二叉树 - 力扣(LeetCode)
给你一棵二叉树的根节点 root ,翻转这棵二叉树,并返回其根节点。
文章讲解:代码随想录 (programmercarl.com)
视频讲解:听说一位巨佬面Google被拒了,因为没写出翻转二叉树 | LeetCode:226.翻转二叉树哔哩哔哩bilibili
递归法(前序)思路:
这个思路比较好想,采用递归要明确递归三部曲
1.确定递归函数的参数和返回值
参数就是要传入节点的指针,不需要其他参数了,通常此时定下来主要参数,如果在写递归的逻辑中发现还需要其他参数的时候,随时补充。
返回值的话其实也不需要,但是题目中给出的要返回root节点的指针,可以直接使用题目定义好的函数,所以就函数的返回类型为TreeNode。
2.确定终止条件
当前节点为空的时候,就返回
if not root : return root
3.确定单层递归的逻辑
因为是先前序遍历,所以先进行交换左右孩子节点,然后反转左子树,反转右子树。
root.left, root.right = root.right, root.left
invertTree(root.left);
invertTree(root.right);基于这递归三步法,代码基本写完
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 root
root.left, root.right = root.right, root.left # 中
self.invertTree(root.left) # 左
self.invertTree(root.right) # 右
return root
迭代法,用栈实现
采用前序,中左右
由于栈结构特性,先进后出,所有我们想要左节点先出来,再加入栈中的时候就先加加入右节点,在用栈实现二叉树过程已经记录过了
这题主要就是在于将栈中弹出的这个节点反转
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
class Solution:
# 迭代法,用栈实现
def invertTree2(self, root: [TreeNode]) -> [TreeNode]:
if not root:
return root
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
感想:
法一中,只要找准递归三要素,法二中只要掌握用栈实现二叉树过程,做好这两点这题其实就没什么难度了 ,主要前面也练习了不少题,慢慢的二叉树实现过程就越来越熟练了
101对称二叉树 (优先掌握递归)
题目链接:101. 对称二叉树 - 力扣(LeetCode)
题目:给定一个二叉树,检查它是否是镜像对称的
示例 1
输入:root = [1,2,2,3,4,4,3]
输出:true示例 2
输入:root = [1,2,2,null,3,null,3]
输出:false
文章讲解:代码随想录 (programmercarl.com)
视频讲解:新学期要从学习二叉树开始! | LeetCode:101. 对称二叉树哔哩哔哩bilibili
思路:
这题要明白:不是某一个节点的左节点与右节点比较,而是左子树与右子树对称
相对于外侧来说,也就是左子树的左节点与右子树的右节点一样
相对于内测来说,就是左子树的右节点与右子树的左节点一样
比较的两个节点,其中有任意一个节点为空,则不对称。或者两个节点均为空,则对称
法一:递归
1.确定递归函数的参数和返回值
因为我们要比较的是根节点的两个子树是否是相互翻转的,进而判断这个树是不是对称树,所以要比较的是两个树,参数自然也是左子树节点和右子树节点。
返回值自然是bool类型。
compare(left,right)
2.确定终止条件
要比较两个节点数值相不相同,首先要把两个节点为空的情况弄清楚!否则后面比较数值的时候就会操作空指针了。
节点为空的情况有:(注意我们比较的其实不是左孩子和右孩子,所以如下我称之为左边节点右边节点)
- 左边节点为空,右边节点不为空,不对称,return false
- 左不为空,右为空,不对称 return false
- 左右都为空,对称,返回true
此时已经排除掉了节点为空的情况,那么剩下的就是左右节点不为空:
- 左右都不为空,比较节点数值,不相同就return false
此时左右节点不为空,且数值也不相同的情况我们也处理了。
3.确定单层递归的逻辑
此时才进入单层递归的逻辑,单层递归的逻辑就是处理 左右节点都不为空,且数值相同的情况。
- 比较二叉树外侧是否对称:传入的是左边节点的左孩子,右边节点的右孩子。
- 比较内侧是否对称,传入左边的右孩子,右边节点的左孩子。
- 如果左右都对称就返回true ,有一侧不对称就返回false 。
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
class Solution:
# 递归
def isSymmetric(self, root: [TreeNode]) -> bool:
if not root:
return True
return self.compare(root.left, root.right)
def compare(self, left: [TreeNode], right: [TreeNode]):
if not left and not right:
return True
elif not left and right:
return False
elif left and not right:
return False
elif left.val != right.val:
return False
outside = self.compare(left.left, right.right) # 左子树左节点,右指数右节点
inside = self.compare(left.right, right.left) # 左字数右节点,右指数左节点
return outside and inside
法二:迭代,用队列实现
遍历每层,将其子节点加入队列时,注意是将需要比较的两个节点,放在一起
比如:先加外侧节点,再加内测节点。或者反过来,先加内侧节点,在家外侧节点
核心就是要将比较的两个节点放一起,连续弹出两个数,比较其是否相等即可
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
class Solution:
# 迭代:使用队列
def isSymmetric2(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 TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
class Solution:
# 迭代:使用栈
def isSymmetric3(self, root: [TreeNode]) -> bool:
if not root:
return True
stack = []
stack.append(root.left)
stack.append(root.right)
while stack:
leftNode = stack.pop()
rightNode = stack.pop()
# 左节点为空、右节点为空,此时说明是对称的
if not leftNode and not rightNode:
continue
# 左右一个节点不为空,或者都不为空但数值不相同,返回false
if not leftNode or not rightNode or leftNode.val != rightNode.val:
return False
stack.append(leftNode.left) # 加入左节点左孩子
stack.append(rightNode.right) # 加入右节点右孩子
stack.append(leftNode.right) # 加入左节点右孩子
stack.append(rightNode.left) # 加入右节点左孩子
return True
法四:迭代,用层序遍历实现
跟前面几种有一点不一样,层次遍历是按层遍历,我们可以将一层的结果放入列表中,比较每一层列表反转后是否一样即可,这里需要注意的是,存入列表时,不存在的要用None占位
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
class Solution:
# 层次遍历
def isSymmetric4(self, root: [TreeNode]) -> bool:
if not root:
return True
queue = collections.deque([root.left,root.right])
while queue:
queue_size = len(queue)
# 直接判断 queue 的长度是不是偶数,不是则False
if queue_size % 2 != 0:
return False
level = []
for i in range(queue_size):
node = queue.popleft()
# 添加元素到队列,没有要添加空值占位
if node:
level.append(node.val)
queue.append(node.left)
queue.append(node.right)
else:
level.append(None)
if level != level[::-1]:
return False
return True
感想:这几题都很好的展示了几种不同数据结构实现二叉树的特点,如果可以用层序遍历,那基本就是套模板。采用递归要注意递归三要素,采用迭代要注意前中后序遍历代码不一样类似,需要自己理清楚