10.层序遍历
层序遍历一个二叉树。就是从左到右一层一层的去遍历二叉树。也就是图论中的广度优先遍历应用在二叉树上。
需要借用一个辅助数据结构即队列来实现,队列先进先出,符合一层一层遍历的逻辑,而用栈先进后出适合模拟深度优先遍历也就是递归的逻辑。
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):
queue=collections.deque([root]) #创建一个双端队列,并把根节点传入
result=[]
if not root: # 根节点是一个列表,如果根节点为空,直接返回
return []
while queue:
level=[] # 存放每一层的节点
size=len(queue)
for _ in range(size):
cur=queue.popleft() # 获取队列队头元素并将其弹出
level.append(cur.val) # 把队头的值存到level列表中
if cur.left: # 如果当前节点有左孩子,就把左孩子加入队列中
queue.append(cur.left)
if cur.right: # 如果当前节点有右孩子,就把右孩子加入队列中
queue.append(cur.right)
result.append(level) #把这一层的节点存到result中
return result
递归法(还需要理解):
递归的基本思路是,从根节点开始,递归地访问每一层的节点,并将它们的值添加到对应层的列表中。递归函数 help 接收三个参数:node 表示当前节点,level 表示当前层的层号,levels 表示存储每一层节点值列表的列表。
递归函数的主要逻辑:
如果当前节点为空(即 not node),则直接返回,结束递归。这是递归的终止条件。
如果 len(levels) 等于 level,说明当前层还没有在 levels 中创建一个空列表,所以在 levels 中添加一个新的空列表,用于存储当前层的节点值。
将当前节点的值添加到对应层的列表中。
递归调用 help 处理左子节点,层号加一。
递归调用 help 处理右子节点,层号加一。
通过递归的方式,这个方法遍历整个二叉树,按照层序将节点值存储在 levels 中,最终返回这个列表,完成了层序遍历的任务。
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):
levels=[] # 定义一个空列表用于存储每一层的节点
self.help(root,0,levels)
return levels
def help(self,node,level,levels):
if not node:
return
if len(levels)==level: # level是节点所在的层,根节点的层数为0
levels.append([])
levels[level].append(node.val)
self.help(node.left,level+1,levels)
self.help(node.right,level+1,levels)
226.翻转二叉树
想要翻转它,其实就把每一个节点的左右孩子交换一下就可以了
递归三步曲:
- 确定递归函数的参数和返回值
- 确定终止条件:当节点为空时,就返回
- 确定单层递归的逻辑
这道题用前序遍历和后序遍历解都可以,但是不能用中序遍历,因为中序遍历是左中右:先把左子树翻转,然后左子树和右子树换位置,接下来处理的右子树其实是第一步已经处理过的左子树,所以用中序遍历的话还有点绕。
前序遍历(递归):
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): # 这里的root其实是指节点,不单单指根节点
if not root:
return None
root.left,root.right=root.right,root.left
self.invertTree(root.left)
self.invertTree(root.right)
return root
后序遍历(递归):
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): # 这里的root指的是节点,不单指根节点
if not root:
return None
self.invertTree(root.left)
self.invertTree(root.right)
root.left,root.right=root.right,root.left
return root
101.对称二叉树
判断一个二叉树是否对称,其实就是判断左右子树是否能够相互翻转(左子树的左孩子和右子树的右孩子是否相等,左子树的右孩子和右子树的左孩子是否相等)
凡是题需要收集子节点的信息返回给父节点的,都采用后序遍历
递归三步曲:
1.确定递归函数的参数和返回值
因为要比较的是根节点的两个子树是否是相互翻转的,进而判断这个树是不是对称树,所以要比较的是两个树,参数自然也是左子树节点和右子树节点。
返回值自然是bool类型。
2.确定终止条件
要比较两个节点数值相不相同,首先要把两个节点为空的情况弄清楚!否则后面比较数值的时候就会操作空指针了。
节点为空的情况有:(注意我们比较的其实不是左孩子和右孩子,所以如下称之为左节点右节点)
- 左节点为空,右节点不为空,不对称,return false
- 左不为空,右为空,不对称 return false
- 左右都为空,对称,返回true
此时已经排除掉了节点为空的情况,那么剩下的就是左右节点不为空:
- 左右都不为空,比较节点数值,不相同就return false
此时左右节点不为空,且数值也不相同的情况我们也处理了
3.确定单层递归的逻辑
此时才进入单层递归的逻辑,单层递归的逻辑就是处理 左右节点都不为空,且数值相同的情况。
- 比较二叉树外侧是否对称:传入的是左节点的左孩子,右节点的右孩子。
- 比较内侧是否对称,传入左节点的右孩子,右节点的左孩子。
- 如果左右都对称就返回true ,有一侧不对称就返回false 。
class Solution:
def isSymmetric(self, root):
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