15代码随想录算法训练营第15天|二叉树 part02| 层序遍历 、226.翻转二叉树、101. 对称二叉树
层序遍历
102. 二叉树的层序遍历
题目链接: 102.二叉树的层序遍历
需要用到队列,队列先进先出,符合一层一层遍历的逻辑,而用栈先进后出适合模拟深度优先遍历也就是递归的逻辑
这种层序遍历方式就是图论中的广度优先遍历
层次遍历动画如下:
代码:
自己做
因为题目中是按层返回结点信息的,如果采用队列的方式,不知道如何按层返回结点信息,所以我就采用了递归的方式,每次返回都是一层的结点信息。
python
# Definition for a binary tree node.
# class TreeNode(object):
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution(object):
def levelOrderTraverse(self, lst, result):
if len(lst) == 0:
return None
else:
sub_list = []
nextLevel_list = []
for node in lst:
sub_list.append(node.val)
if node.left: nextLevel_list.append(node.left)
if node.right: nextLevel_list.append(node.right)
result.append(sub_list)
return self.levelOrderTraverse(nextLevel_list, result)
def levelOrder(self, root):
"""
:type root: TreeNode
:rtype: List[List[int]]
"""
# 思想:
'''
1. 创建一个队列
2. 只要队列不等于空,那么出队,然后将其左孩子和右孩子送入队列中,知道队列为空
注意:如何记录哪些结点属于哪一层呢?
好像递归更好记录层次信息,每次return 层次加1
'''
result = []
lst = []
if root:
lst.append(root)
else:
return []
self.levelOrderTraverse(lst, result)
return result
代码随想录
采用队列的方式:
for _ in range(len(queue)): # 点睛之笔!!!只遍历每层结点的长度
python
from collections import deque
# Definition for a binary tree node.
# class TreeNode(object):
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution(object):
def levelOrder(self, root):
"""
:type root: TreeNode
:rtype: List[List[int]]
"""
if not root:
return []
queue = collections.deque([root]) # 这里root要加[],因为deque类接受数据类型必须为可迭代数据类型
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
递归的方式:
这个递归方式没有自己写的好理解
# Definition for a binary tree node.
# class TreeNode(object):
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution(object):
def levelOrder(self, root):
"""
:type root: TreeNode
:rtype: List[List[int]]
"""
levels = []
self.helper(root, 0, levels)
return levels
def helper(self, node, level, levels):
if not node:
return
if len(levels) == level: # 每开始遍历一个新层,要先添加一个空列表,再往空列表中添加元素
levels.append([])
levels[level].append(node.val)
self.helper(node.left, level + 1, levels)
self.helper(node.right, level + 1, levels)
接下来为10道用到层次遍历的题
107.二叉树的层次遍历 II
反转result列表即可
result[::-1]
199.二叉树的右视图
方法一:
return [result[-1] for result in result]
方法二:
result.append(level[-1]) # level为每层的结点列表
return result
637.二叉树的层平均值
改进方法一:(时间复杂度会很高!)
result.append(float(sum(level))/len(level))
return result
改进方法二:
level_sum = 0.0
level_len = 0
for _ in range(len(queue)): # 点睛之笔!!!只遍历每层结点的长度
cur = queue.popleft()
level_sum += cur.val
level_len += 1
if cur.left:
queue.append(cur.left)
if cur.right:
queue.append(cur.right)
result.append(level_sum/level_len)
429. N 叉树的层序遍历
思想:
框架与二叉树的层次遍历一致
注意
collections.deque是一个类,如果直接传参进行初始化,需要传入可迭代的对象
方式一:
queue = collections.deque([root])
方式二:
queue = collections.deque()
queue.append(root)
n叉树的children为可迭代对象,需要使用for循环进行遍历
代码:
python
from collections import deque
"""
# Definition for a Node.
class Node(object):
def __init__(self, val=None, children=None):
self.val = val
self.children = children
"""
class Solution(object):
def levelOrder(self, root):
"""
:type root: Node
:rtype: List[List[int]]
"""
if not root:
return []
queue = deque()
queue.append(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.在每个树行中找最大值
代码改写部分:
python
cur_level_max = -2e31
for _ in range(len(queue)): # 点睛之笔!!!只遍历每层结点的长度
cur = queue.popleft()
if cur.val > cur_level_max: cur_level_max = cur.val
if cur.left:
queue.append(cur.left)
if cur.right:
queue.append(cur.right)
result.append(cur_level_max)
116. 填充每个节点的下一个右侧节点指针
思想:
这道题可能是脑子不好用了,没有做出来…
本题依然是层序遍历,只不过在单层遍历的时候记录一下 本层的头部节点 ,然后在遍历的时候让前一个节点指向本节点就可以了
代码:
python
from collections import deque
"""
# Definition for a Node.
class Node(object):
def __init__(self, val=0, left=None, right=None, next=None):
self.val = val
self.left = left
self.right = right
self.next = next
"""
class Solution(object):
def connect(self, root):
"""
:type root: Node
:rtype: Node
"""
if not root:
return root
queue = deque()
queue.append(root)
while queue:
level_size = len(queue)
prev = None
for i in range(level_size):
node = queue.popleft()
# 重要的步骤
if prev:
prev.next = node
prev = node
if node.left:
queue.append(node.left)
if node.right:
queue.append(node.right)
return root
117.填充每个节点的下一个右侧节点指针II
同上一题
这道题目说是二叉树,但116题目说是完整二叉树,其实没有任何差别,一样的代码一样的逻辑一样的味道
104.二叉树的最大深度
思想:
有几层的问题
使用迭代法的话,使用层序遍历是最为合适的,因为最大的深度就是二叉树的层数,和层序遍历的方式极其吻合。
111.二叉树的最小深度
思想:
需要注意的是,只有当左右孩子都为空的时候,才说明遍历的最低点了。如果其中一个孩子为空则不是最低点
我当时想的是如果当前层的结点数量小于2**(level-1) 的话就说明到了最小深度,这种想法是不对的。反例:一条斜线的二叉树
代码:
python
# Definition for a binary tree node.
# class TreeNode(object):
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution(object):
def minDepth(self, root):
"""
:type root: TreeNode
:rtype: 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
致敬叶师傅!(连着打了10个小怪,真累坏了)
噗,终于结束了…
226.翻转二叉树
自己做
思想:
从上到下遍历和从下到上遍历应该是一样的
使用递归:
- 传入参数:root
- 单层操作:交换左右结点
- 返回条件:无左右孩子结点
代码:
python
# Definition for a binary tree node.
# class TreeNode(object):
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution(object):
def invertTree(self, root):
"""
:type root: TreeNode
:rtype: TreeNode
"""
# 从上到下遍历和从下到上遍历应该是一样的
if root :
cur = root
self.helper(cur)
return root
def helper(self, cur):
if cur.left != None or cur.right != None:
temp = cur.left
cur.left = cur.right
cur.right = temp
if cur.left != None:
self.helper(cur.left)
if cur.right != None:
self.helper(cur.right)
if cur.left == None and cur.right == None:
return
代码随想录
思想:
只要把每一个节点的左右孩子翻转一下,就可以达到整体翻转的效果
这道题目使用前序遍历和后序遍历都可以,唯独中序遍历不方便,因为中序遍历会把某些节点的左右孩子翻转了两次!建议拿纸画一画,就理解了
那么**层序遍历可以不可以呢?依然可以的!**只要把每一个节点的左右孩子翻转一下的遍历方式都是可以的!
注意
二叉树解题的大忌就是自己稀里糊涂的过了(因为这道题相对简单),但是也不知道自己是怎么遍历的。
针对翻转二叉树,我给出了一种递归,三种迭代(两种模拟深度优先遍历,一种层序遍历)的写法,都是之前我们讲过的写法,融汇贯通一下而已。
大家一定也有自己的解法,但一定要成方法论,这样才能通用,才能举一反三!
代码:
递归法
# 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
迭代法:前序遍历:
# 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.left:
stack.append(node.left)
if node.right:
stack.append(node.right)
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
self.invertTree(root.left)
root.left, root.right = root.right, root.left
self.invertTree(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()
if node.left:
stack.append(node.left)
node.left, node.right = node.right, node.left
if node.left:
stack.append(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
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()
if node.left:
stack.append(node.left)
if node.right:
stack.append(node.right)
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:
for i 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. 对称二叉树
自己做
思想:
代码:
代码随想录
思想:
本题遍历只能是“后序遍历”,因为我们要通过递归函数的返回值来判断两个子树的内侧节点和外侧节点是否相等。
正是因为要遍历两棵树而且要比较内侧和外侧节点,所以准确的来说是一个树的遍历顺序是左右中,一个树的遍历顺序是右左中。
代码:
python
递归法
递归三部曲:
-
确定递归函数的参数和返回值
-
确定终止条件
-
确定单层递归的逻辑
# Definition for a binary tree node.
# class TreeNode(object):
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution(object):
def isSymmetric(self, root):
"""
:type root: TreeNode
:rtype: bool
"""
if not root:
return False
return self.compare(root.left, root.right)
def compare(self, left, right): # 1. 确定递归函数的参数和返回值
# 2. 确定终止条件
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
# 3. 确定单层递归的逻辑
outside = self.compare(left.left, right.right)
inside = self.compare(left.right, right.left)
return outside and inside
迭代法
- 使用队列
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
- 层次遍历
class Solution:
def isSymmetric(self, root: TreeNode) -> bool:
if not root:
return True
queue = collections.deque([root.left, root.right])
while queue:
level_size = len(queue)
if level_size % 2 != 0:
return False
level_vals = []
for i in range(level_size):
node = queue.popleft()
if node:
level_vals.append(node.val)
queue.append(node.left)
queue.append(node.right)
else:
level_vals.append(None)
if level_vals != level_vals[::-1]:
return False
return True