Leetcode刷题笔记15:二叉树2(层次遍历题目串烧)

178 阅读8分钟

导语

leetcode刷题笔记记录,本篇博客记录二叉树部分的题目,主要题目包括:

二叉树的层次遍历

上面的前10道题目都是层次遍历的题目及其变种,这里先看一下层次遍历的代码:

Leetcode 102.二叉树的层次遍历

from collections import deque

class Solution:
    def levelOrder(self, root: Optional[TreeNode]) -> List[List[int]]:
        result = []
        queue = deque()
        if root:
            queue.append(root)
        while queue:
            items = []
            for i in range(len(queue)):
                node = queue.popleft()
                items.append(node.val)
                if node.left: queue.append(node.left)
                if node.right: queue.append(node.right)
            result.append(items)
            
        return result

使用collections中的deque构造双向队列,首先将root入队,接着进入while循环,循环的终止条件就是队列为空。然后进入for循环,逐个出队,每出队一个元素,就把该元素的左孩子和右孩子分别入队。每一层的元素放到一个列表中,之后再将该层元素append到最终的完整结果中。

基于以上模板,其他题目也很容易做出来

Leetcode 107.二叉树的层次遍历II

这道题目直接反向最终result即可。

from collections import deque

class Solution:
    def levelOrderBottom(self, root: Optional[TreeNode]) -> List[List[int]]:
        queue = deque()
        result = []
        if root:
            queue.append(root)
        while queue:
            items = []
            for i in range(len(queue)):
                node = queue.popleft()
                items.append(node.val)
                if node.left: queue.append(node.left)
                if node.right: queue.append(node.right)
            result.append(items)
        return result[::-1]

Leetcode 199.二叉树的右视图

类似的,这道题目直接取每行的最后一个元素。

from collections import deque

class Solution:
    def rightSideView(self, root: Optional[TreeNode]) -> List[int]:
        result = []
        queue = deque()
        if root:
            queue.append(root)
        while queue:
            items = []
            size = len(queue)
            for _ in range(size):
                node = queue.popleft()
                items.append(node.val)
                if node.left: queue.append(node.left)
                if node.right: queue.append(node.right)
            
            result.append(items[-1])
        return result

Leetcode 637.二叉树的层平均值

类似的,这道题目直接取每行的平均值。

from collections import deque


class Solution:
    def rightSideView(self, root: Optional[TreeNode]) -> List[int]:
        result = []
        queue = deque()
        if root:
            queue.append(root)
        while queue:
            items = []
            size = len(queue)
            for _ in range(size):
                node = queue.popleft()
                items.append(node.val)
                if node.left: queue.append(node.left)
                if node.right: queue.append(node.right)
            
            result.append(items[-1])
        return result

Leetcode 429.N叉树的层序遍历

这道题目在判断孩子节点是否为空时需要修改一下,变成for循环。

from collections import deque

class Solution:
    def levelOrder(self, root: 'Node') -> List[List[int]]:
        result = []
        queue = deque()
        if root:
            queue.append(root)
        while queue:
            items = []
            size = len(queue)
            for _ in range(size):
                node = queue.popleft()
                items.append(node.val)
                if node.children:
                    for n in node.children:
                        queue.append(n)
            result.append(items)
        return result

Leetcode 515.在每个树行中找最大值

类似的,取每行的最大值即可。

from collections import deque

class Solution:
    def largestValues(self, root: Optional[TreeNode]) -> List[int]:
        result = []
        queue = deque()
        if root:
            queue.append(root)
        while queue:
            items = []
            size = len(queue)
            for _ in range(size):
                node = queue.popleft()
                items.append(node.val)
                if node.left: queue.append(node.left)
                if node.right: queue.append(node.right)
            result.append(max(items))
        return result

Leetcode 116.填充每个节点的下一个右侧节点指针

这道题目返回值还是原来的树,不过需要注意的几个点如下:

  1. next本身就是空的;
  2. popleft出队后,队首元素就是当前出队元素的next,直接连接即可。
"""
# 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
"""
from collections import deque

class Solution:
    def connect(self, root: 'Optional[Node]') -> 'Optional[Node]':
        queue = deque()
        if root:
            queue.append(root)
        while queue:
            size = len(queue)
            for i in range(size):
                node = queue.popleft()
                if i < size - 1:
                    node.next = queue[0]
                if node.left: 
                    queue.append(node.left)
                    queue.append(node.right)
        return root

Leetcode 117.填充每个节点的下一个右侧节点指针II

与上面的类似,只不过不是完美二叉树,所以需要多一个判断条件if node.right。

from collections import deque

class Solution:
    def connect(self, root: 'Node') -> 'Node':
        queue = deque()
        if root:
            queue.append(root)
        while queue:
            size = len(queue)
            for i in range(size):
                node = queue.popleft()
                if i < size - 1:
                    node.next = queue[0]
                if node.left: queue.append(node.left)
                if node.right: queue.append(node.right)

        return root

Leetcode 104.二叉树的最大深度

这个只需要判断大循环(外层的while循环了几次就行),代码如下:

from collections import deque


class Solution:
    def maxDepth(self, root: Optional[TreeNode]) -> int:
        cnt = 0
        queue = deque()
        if root: 
            queue.append(root)
        while queue:
            size = len(queue)
            for _ in range(size):
                node = queue.popleft()
                if node.left: queue.append(node.left)
                if node.right: queue.append(node.right)
            cnt += 1

        return cnt

Leetcode 111.二叉树的最小深度

这个最小深度,实际上也是大循环的次数,但是要求碰到一个节点的左右孩子都为空时直接跳出大循环。

from collections import deque

class Solution:
    def minDepth(self, root: Optional[TreeNode]) -> int:
        cnt = 0
        queue = deque()
        if root:
            queue.append(root)
        while queue:
            size = len(queue)
            cnt += 1
            for _ in range(size):
                node = queue.popleft()
                if node.left is None and node.right is None:
                    return cnt
                if node.left: queue.append(node.left)
                if node.right: queue.append(node.right)
        return cnt

Leetcode 226 翻转二叉树

题目描述

给你一棵二叉树的根节点 root ,翻转这棵二叉树,并返回其根节点。 示例 1:

输入: root = [4,2,7,1,3,6,9]
输出: [4,7,2,9,6,3,1]

解法

这道题目可以采用递归或者层次遍历、迭代等多种方法解决。下面介绍递归和层次遍历法:

递归

递归可以采用前序遍历、后序遍历和中序遍历(不推荐),其主要实现如下:

# 前序遍历
class Solution:
    def invertTree(self, root: Optional[TreeNode]) -> Optional[TreeNode]:
        if root is None:
            return root
            
        root.left, root.right = root.right, root.left
        self.invertTree(root.left)
        self.invertTree(root.right)

        return root
# 后序遍历
class Solution:
    def invertTree(self, root: Optional[TreeNode]) -> Optional[TreeNode]:
        if root is None:
            return root
        
        self.invertTree(root.left)
        self.invertTree(root.right)
        root.left, root.right = root.right, root.left

        return root

采用中序遍历时,一个主要的坑在于如果还是按上述写法更改交换位置的顺序,那么会重复操作某一侧的子树,为此,中序遍历应该操作同一侧,代码如下:

# 中序遍历
class Solution:
    def invertTree(self, root: Optional[TreeNode]) -> Optional[TreeNode]:
        if root is None:
            return root
        
        self.invertTree(root.left)
        root.left, root.right = root.right, root.left
        self.invertTree(root.left)


        return root

同样的,这道题目也可以通过层次遍历来解决,其核心思想就是层次遍历的过程中,交换节点的左右孩子,对所有的节点都这么做。

from collections import deque

class Solution:
    def invertTree(self, root: Optional[TreeNode]) -> Optional[TreeNode]:
        queue = deque()
        if root:
            queue.append(root)
        while queue:
            size = len(queue)
            for i in range(size):
                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

Leetcode 101 对称二叉树

题目描述

给你一个二叉树的根节点 root , 检查它是否轴对称。

示例 1:

输入: root = [1,2,2,3,4,4,3]
输出: true

解法

首先介绍递归解法,观察图片可以看出,如果我们要比较对称,那么一定是要比较左侧节点的左孩子与右侧节点的右孩子相等,右侧节点的左孩子和左侧节点的右孩子相等。所以,定义递归函数的输入是一个left节点和一个right节点,递归的终止条件则是left和right为空,代码如下:

class Solution:
    def isSymmetric(self, root: Optional[TreeNode]) -> bool:
        if root is None:
            return True
        else:
            return self.compare(root.left, root.right)
    
    def compare(self, left, right):
        if left is None and right is None:
            return True
        elif left is None and right is not None:
            return False
        elif left is not None and right is None:
            return False
        elif left.val != right.val:
            return False
        else:
            outside = self.compare(left.left, right.right)
            inside = self.compare(left.right, right.left)
            return outside and inside

类似的,可以写出层次遍历的解法:

from collections import deque

class Solution:
    def isSymmetric(self, root: Optional[TreeNode]) -> bool:
        # 层次遍历法
        if not root:
            return True
        queue = deque([root.left, root.right])
        while queue:
            size = len(queue)
            if size % 2:
                return False
            level_values = []
            for _ in range(size):
                node = queue.popleft()
                if node:
                    level_values.append(node.val)
                    queue.append(node.left)
                    queue.append(node.right)
                else:
                    level_values.append(None)
            if level_values != level_values[::-1]:
                return False
        return True

Leetcode 100 相同的树

题目描述

给你两棵二叉树的根节点 p 和 q ,编写一个函数来检验这两棵树是否相同。如果两个树在结构上相同,并且节点具有相同的值,则认为它们是相同的。

示例 1:

输入: p = [1,2,3], q = [1,2,3]
输出: true

解法

踩坑

这道题目在做的时候我踩了一个大坑,就是我以为只要保证二叉树的前序、中序、后序和层次遍历的结果相同,就可以保证他们完全相同,为此我写了这样一个代码:

from collections import deque

class Solution:
    def isSameTree(self, p: Optional[TreeNode], q: Optional[TreeNode]) -> bool:
        def pre_order_traversal(root):
            if not root:
                return []
            left = pre_order_traversal(root.left)
            right = pre_order_traversal(root.right)
            return [root.val] + left + right
        def mid_order_traversal(root):
            if not root:
                return []
            left = mid_order_traversal(root.left)
            right = mid_order_traversal(root.right)
            return left + [root.val] + right
        def post_order_traversal(root):
            if not root:
                return []
            left = mid_order_traversal(root.left)
            right = mid_order_traversal(root.right)
            return left + right + [root.val] 
        def layer_traversal(root):
            queue = deque()
            result = []
            if root:
                queue.append(root)
            while queue:
                items = []
                size = len(queue)
                for _ in range(size):
                    node = queue.popleft()
                    items.append(node.val)
                    if node.left: queue.append(node.left)
                    if node.right: queue.append(node.right)
                result.append(items)
            return result
        p_pre, p_mid, p_post, p_layer = pre_order_traversal(p), mid_order_traversal(p), post_order_traversal(p), layer_traversal(p)
        q_pre, q_mid, q_post, q_layer = pre_order_traversal(q), mid_order_traversal(q), post_order_traversal(q), layer_traversal(q)

        return p_pre == q_pre and p_mid == q_mid and p_post == q_post and p_layer == q_layer

结果,总是通不过一个测试样例,即

p=[1,1],q=[1,null,1]

这时,我才想到,二叉树中的值是可能存在重复值的!,所以这道题目不能通过这种方式判断,起码要考虑None的情况才可以。于是,我进行了一步改进,即考虑节点不存在为None时,我也将其加入层次遍历值的列表中。这样,只使用层次遍历的结果就能判断是否相同。

from collections import deque

class Solution:
    def isSameTree(self, p: Optional[TreeNode], q: Optional[TreeNode]) -> bool:
        def layer_traversal(root):
            queue = deque()
            result = []
            if root:
                queue.append(root)
            while queue:
                items = []
                size = len(queue)
                for _ in range(size):
                    node = queue.popleft()
                    if node:
                        items.append(node.val)
                    else:
                        items.append(None)
                        continue
                    if node.left: 
                        queue.append(node.left) 
                    else:
                        queue.append(None)
                    if node.right: 
                        queue.append(node.right) 
                    else: 
                        queue.append(None)
                result.append(items)
            return result
        p_layer = layer_traversal(p)
        q_layer = layer_traversal(q)

        return  p_layer == q_layer

当然,这道题的简单解法或者更容易理解的版本应该使用递归,即判断每个节点的左右节点是否相同。

from collections import deque

class Solution:
    def isSameTree(self, p: Optional[TreeNode], q: Optional[TreeNode]) -> bool:
        if p is None and q is None:
            return True
        elif p is None or q is None:
            return False
        elif p.val != q.val:
            return False
        else:
            left = self.isSameTree(p.left, q.left)
            right = self.isSameTree(p.right, q.right)
            return left and right

Leetcode 572 另一棵树的子树

题目描述

给你两棵二叉树 root 和 subRoot 。检验 root 中是否包含和 subRoot 具有相同结构和节点值的子树。如果存在,返回 true ;否则,返回 false 。二叉树 tree 的一棵子树包括 tree 的某个节点和这个节点的所有后代节点。tree 也可以看做它自身的一棵子树。

示例 1:

输入: root = [3,4,5,1,2], subRoot = [4,1,2]
输出: true

解法

这道题目完全可以借助上面的判断两棵树完全相同的方法,使用递归来解决,即我们只要判断两棵树是否完全一致,或者小树是不是大树的左/右子树的子树。

结束的条件及大树或小树变为None。具体代码如下:

class Solution:
    def isSubtree(self, root: Optional[TreeNode], subRoot: Optional[TreeNode]) -> bool:
        if root is None and subRoot is None:
            return True
        elif root is None or subRoot is None:
            return False
        else:
            return self.isSameTree(root, subRoot) or self.isSubtree(root.left, subRoot) or self.isSubtree(root.right, subRoot)

    
    def isSameTree(self, p: Optional[TreeNode], q: Optional[TreeNode]) -> bool:
        if p is None and q is None:
            return True
        elif p is None or q is None:
            return False
        elif p.val != q.val:
            return False
        else:
            left = self.isSameTree(p.left, q.left)
            right = self.isSameTree(p.right, q.right)
            return left and right