二叉树的层序遍历
文档讲解:代码随想录
题目链接:. - 力扣(LeetCode)
二叉树每一层的数值放在一个数组里,最后返回的就是一个二维数组
在二叉树本身的结构中,我们是无法做到层序遍历的,如下图,第一层我们可以遍历完,但是当遍历到第二层,9和20之间是没有联系的,遍历完9是无法遍历到20的。或者说我们遍历20时,要把9保存起来,并且知道它们在同一层。
我们借助一个队列来遍历二叉树,先遍历到的元素就是我们最后数组中的节点顺序,会先保存起来。我们要记录每一层的大小,并且要知道下一层的元素有哪些,就需要在队列中弹出元素的同时加入该元素的左右节点,因为此时队列中不仅有当前层的元素还有下一层的元素,所以我们会利用之前记录的每一层的大小,弹出相应的节点,即为当前层的元素
class Solution:
def levelOrder(self, root: Optional[TreeNode]) -> List[List[int]]:
result = [] #最终的结果数组
if not root:
return []
#定义一个队列
queue = collections.deque([root])
while queue:
mid = []#记录每一层的数值
size = len(queue) #记录每一层大小
for _ in range(size):
node = queue.popleft() #右入左出
mid.append(node.val)
if node.left:
queue.append(node.left) #加入相应节点的左节点,这里可能是空加入到队列之后,下面弹出的时候node是none会报错
if node.right:
queue.append(node.right) #加入相应节点的右节点
result.append(mid)
return result
注:
deque 数据类型来自于collections 模块,支持从头和尾部的常数时间 append/pop 操作。若使用 Python 的 list,通过 list.pop(0) 去除头部会消耗 O(n)的时间。
226.翻转二叉树
文档讲解:代码随想录
题目链接:. - 力扣(LeetCode)
只要交换每一个节点对应的左右节点即可。按什么顺序来处理每一个节点呢,可以采用深度优先(前中后序)或者广度优先
深度优先遍历(递归)
前序遍历
中左右
class Solution:
def invertTree(self, root: Optional[TreeNode]) -> Optional[TreeNode]:
def reverse(root):
if not root: # 如果根节点为空
return [] # 返回空列表,结束递归
root.left, root.right = root.right, root.left # 交换左右子树
reverse(root.left) # 递归处理左子树
reverse(root.right) # 递归处理右子树
reverse(root) # 调用递归函数
return root # 返回反转后的根节点
中序遍历
左中右
如果按照前序遍历的改,那么本来一开始处理的就是左子树, 再处理中交换左右结点之后,左子树变成了现在的右子树,接下来处理右子树,遍历的就是原来的左子树,那么右子树就是没有处理
所以最后应该处理的还是左子树,也就是交换之前的右子树
###中序遍历
class Solution:
def invertTree(self, root: Optional[TreeNode]) -> Optional[TreeNode]:
def reverse(root):
if not root: #1
return []#2
reverse(root.left)#3
root.left,root.right = root.right,root.left#4
reverse(root.left)#5
reverse(root)
return root
后序遍历
###后序遍历
class Solution:
def invertTree(self, root: Optional[TreeNode]) -> Optional[TreeNode]:
def reverse(root):
if not root: #1
return []#2
reverse(root.left)#3
reverse(root.right)#4
root.left,root.right = root.right,root.left#5
reverse(root)
return root
广度优先遍历(层序)
class Solution:
def invertTree(self, root: Optional[TreeNode]) -> Optional[TreeNode]:
if not root:
return None
deque = collections.deque([root])
while deque:
###翻转每一层
for _ in range(len(deque)):
node = deque.popleft() #该层的节点
node.left,node.right = node.right,node.left #翻转每一个遍历到的节点
if node.left:
deque.append(node.left)
if node.right:
deque.append(node.right)
return root
101. 对称二叉树
文档讲解:代码随想录
题目链接:. - 力扣(LeetCode)
没有怎么懂┭┮﹏┭┮
要点: 从根节点出发的两个节点(node1,node2)。node1如果向左,那么node2向右的值就得与其相等;如果node1向右,那么node2向左的值就与其相等。即我们要同时判断node1->left == node2->right和node1->right == node2->left。如果两者皆满足,那这就是一个对称的二叉树。
二叉树最终是考察我们如何遍历的, 那么我们应该使用前中后序哪一个遍历方式呢.只能使用后序(左右中)。 只有后序遍历,才能把底部孩子的信息 返回给上一层
对于二叉树是否对称,要比较的是根节点的左子树与右子树是不是相互翻转的,理解这一点就知道了其实我们要比较的是两个树(这两个树是根节点的左右子树),所以在递归遍历的过程中,也是要同时遍历两棵树。我们的递归函数的传入参数就是左右子树
明确递归什么时候结束:
①左右都为空,肯定是对称的:返回true
②有一个为空一个不为空,肯定不是对称的了:返回false ③都不为空且值相等,那么要继续递归判断
class Solution:
def isSymmetric(self, root: Optional[TreeNode]) -> bool:
if not root:
return False
return self.compare(root.left,root.right)
def compare(self,left,right):
if left ==None and right != None:
return False
if left !=None and right == None:
return False
if left ==None and right==None:
return True
if left.val != right.val:
return False
#在比较外面的过程中,如果遇到了不对称的情况就直接返回了false
outside = self.compare(left.left,right.right)
##在比较里面的过程中,如果遇到了不对称的情况就直接返回了false
inside = self.compare(left.right,right.left)
return outside and inside
补充:感觉递归的主要作用是判断当前两个节点值是否满足对称的要求,我们主要的要写的就是如何遍历值,也就是如何传给递归函数实参,outside和inside,只要在遍历过程中有一个是false了,那么这个false就会影响到最后的结果是false
更新
后续新理解:递归的作用就是判断两颗二叉树是否可以翻转,就要比较是否node1->left == node2->right和node1->right == node2->left,如果不等,就可以判断出不可以翻转了,如果相等,那么接下来还要继续判断,此时就可以将2个二叉树分解为4个二叉树了,两两比较,分别是外外和内内,内外都可以翻转才是翻转二叉树