Bootstrap

代码随想录算法训练营第十四天 | 226.翻转二叉树,101. 对称二叉树, 104.二叉树的最大深度 , 111.二叉树的最小深度

目录

226.翻转二叉树

思路

方法一: 前序遍历

方法二:中序遍历

方法三:后序遍历

方法四:层序遍历

心得收获 

101. 对称二叉树

思路

方法一:递归遍历

方法二:迭代法,使用两个队列

方法三:迭代法,使用栈和单队列

 方法四:层序遍历

心得收获

104.二叉树的最大深度

思路

方法一:递归遍历

方法二:层序遍历

 心得收获

111.二叉树的最小深度 

思路

方法一:递归遍历,使用后序

方法二:递归,使用前序

方法三:迭代法

心得收获


226.翻转二叉树

思路

想要翻转它,其实就把每一个节点的左右孩子交换一下就可以了。

关键在于遍历顺序,前中后序应该选哪一种遍历顺序? (一些同学这道题都过了,但是不知道自己用的是什么顺序)

遍历的过程中去翻转每一个节点的左右孩子就可以达到整体翻转的效果。

注意只要把每一个节点的左右孩子翻转一下,就可以达到整体翻转的效果

这道题目使用前序遍历和后序遍历都可以,唯独中序遍历不方便,因为中序遍历会把某些节点的左右孩子翻转了两次!建议拿纸画一画,就理解了

那么层序遍历可以不可以呢?依然可以的!只要把每一个节点的左右孩子翻转一下的遍历方式都是可以的!

方法一: 前序遍历

# 迭代法前序遍历
class Solution:
    def invertTree(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.left:stack.append(node.left)
            if node.right:stack.append(node.right)
        return root
    
# 递归法前序遍历
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
    

方法二:中序遍历

# 迭代法中序遍历
class Solution:
    def invertTree(self,root:TreeNode) -> TreeNode:
        if not root:
            return root
        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
    
# 递归法中序遍历
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
    

方法三:后序遍历

# 迭代法后序遍历
class Solution:
    def invertTree(self,root:TreeNode) -> TreeNode:
        if not root:
            return root
        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
    
# 递归法后序遍历
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

方法四:层序遍历

from collections import deque
# 层序遍历
class Solution:
    def invertTree(self,root:TreeNode) -> TreeNode:
        if not root:
            return root
        queue = deque([root])
        while queue:
            size = len(queue)
            for _ in range(size):
                node = queue.popleft()
                node.right,node.left = node.left,node.right
                if node.right:
                    queue.append(node.right)
                if node.left:
                    queue.append(node.left)
        return root
    

心得收获 

使用递归的中序遍历,某些节点的左右孩子会翻转两次。避免此问题的解决办法就是在递归右孩子的时候,依然要写成self.invertTree(root.left),因为此时左右节点已经做了交换。代码虽然可以,但这毕竟不是真正的递归中序遍历了。

为什么这个迭代法中序就是可以的呢,因为这是用栈来遍历,而不是靠指针来遍历,避免了递归法中翻转了两次的情况。

101. 对称二叉树

思路

首先想清楚,判断对称二叉树要比较的是哪两个节点,要比较的可不是左右节点!

对于二叉树是否对称,要比较的是根节点的左子树与右子树是不是相互翻转的,理解这一点就知道了其实我们要比较的是两个树(这两个树是根节点的左右子树),所以在递归遍历的过程中,也是要同时遍历两棵树。

那么如何比较呢?

比较的是两个子树的里侧和外侧的元素是否相等。如图所示:

101. 对称二叉树1

那么遍历的顺序应该是什么样的呢?

本题遍历只能是“后序遍历”,因为我们要通过递归函数的返回值来判断两个子树的内侧节点和外侧节点是否相等。

正是因为要遍历两棵树而且要比较内侧和外侧节点,所以准确的来说是一个树的遍历顺序是左右中,一个树的遍历顺序是右左中。

但都可以理解算是后序遍历,尽管已经不是严格上在一个树上进行遍历的后序遍历了。

方法一:递归遍历

# 使用递归
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) -> bool:
        #首先排除空节点的情况
        if left and not right:
            return False
        elif not left and right:
            return False
        elif not left and not right:
            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 == inside
        return isSame 

方法二:迭代法,使用两个队列

# 迭代法: 使用队列
from collections import deque
class Solution:
    def isSymmetric(self,root:TreeNode) -> bool:
        if not root:
            return True
        left_queue = deque([root.left])
        right_queue = deque([root.right])
        while left_queue and right_queue:
            left = left_queue.popleft()
            right = right_queue.popleft()
            if left and not right:
                return False
            elif not left and right:
                return False
            elif not left and not right:
            # 这个地方不能直接返回True,因为此时有可能队列还不为空,需要进行后面的判断
                continue
            elif left.val != right.val:
                return False
            left_queue.append(left.left)
            left_queue.append(left.right)
            right_queue.append(right.right)
            right_queue.append(right.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 = deque()
        queue.append(root.left)
        queue.append(root.right)
        while queue:
            size = len(queue)
            if size % 2 != 0: return False
            level_val = []
            for _ in range(len(queue)):
                node = queue.popleft()
                if node:
                    level_val.append(node.val)
                    queue.append(node.left)
                    queue.append(node.right)
                else:
                    level_val.append(None)
            
            if level_val == level_val[::-1]:
                return False
        return True

心得收获

层序遍历的最后判断逻辑没有想出来怎么写,感觉自己经验不足,希望后续能加油!

104.二叉树的最大深度

思路

建议大家先看视频,看完之后就能理解为什么中序遍历不能求高度和深度。

本题可以使用前序(中左右),也可以使用后序遍历(左右中),使用前序求的就是深度,使用后序求的是高度。

  • 二叉树节点的深度:指从根节点到该节点的最长简单路径边的条数或者节点数(取决于深度从0开始还是从1开始)
  • 二叉树节点的高度:指从该节点到叶子节点的最长简单路径边的条数或者节点数(取决于高度从0开始还是从1开始)

而根节点的高度就是二叉树的最大深度,所以本题中我们通过后序求的根节点高度来求的二叉树最大深度。

方法一:递归遍历

# 递归法
class Solution:
    def maxDepth(self,root:TreeNode) -> int:
        if not root:
            return 0
        return self.getHeight(root)

    def getHeight(self,node:TreeNode) -> int:
        if node is None:
            return 0
        leftHeight = self.getHeight(node.left)
        right_height = self.getHeight(node.right)
        height = max(leftHeight,right_height)
        return height+1

# 递归精简
class Solution:
    def maxdepth(self, root: treenode) -> int:
        if not root:
            return 0
        return 1 + max(self.maxdepth(root.left), self.maxdepth(root.right))

方法二:层序遍历

# 层序遍历
from collections import deque
class Solution:
    def maxDepth(self,root:TreeNode) -> int:
        if not root:
            return 0
        queue = deque([root])
        count = 0
        while queue:
            size = len(queue)
            count += 1
            for _ in range(size):
                node = queue.popleft()
                if node.left:queue.append(node.left)
                if node.right:queue.append(node.right)

        return count

 心得收获

为什么使用中序遍历求不出高度和深度呢,因为需要通过判断左右子节点的高度来确定中间节点的高度,以此来返回上一层。如果使用中序,则中间节点判断的时候还没得到右节点的高度。

111.二叉树的最小深度 

思路

直觉上好像和求最大深度差不多,其实还是差不少的。

本题还有一个误区,在处理节点的过程中,最大深度很容易理解,最小深度就不那么好理解,如图:

111.二叉树的最小深度

这就重新审题了,题目中说的是:最小深度是从根节点到最近叶子节点的最短路径上的节点数量。注意是叶子节点

什么是叶子节点,左右孩子都为空的节点才是叶子节点!

方法一:递归遍历,使用后序

# 递归法
class Solution:
    def minDepth(self,root:TreeNode) -> int:
        if not root:
            return 0
        return self.getHeight(root)
    def getHeight(self,node:TreeNode) -> int:
        if node is None:
            return 0
        left_height = self.getHeight(node.left)
        right_height = self.getHeight(node.right)

        if node.left is None and node.right is not None:
            return right_height + 1
        if node.left is not None and node.right is None :
            return left_height + 1
        
        result = 1 + min(left_height,right_height)
        return result
# 递归精简
class Solution:
    def minDepth(self, root):
        if root is None:
            return 0
        if root.left is None and root.right is not None:
            return 1 + self.minDepth(root.right)
        if root.left is not None and root.right is None:
            return 1 + self.minDepth(root.left)
        return 1 + min(self.minDepth(root.left), self.minDepth(root.right))

方法二:递归,使用前序

class Solution:
    def __init__(self):
        self.result = float('inf')

    def getDepth(self, node, depth):
        if node is None:
            return
        if node.left is None and node.right is None:
            self.result = min(self.result, depth)
        if node.left:
            self.getDepth(node.left, depth + 1)
        if node.right:
            self.getDepth(node.right, depth + 1)

    def minDepth(self, root):
        if root is None:
            return 0
        self.getDepth(root, 1)
        return self.result

方法三:迭代法

# 迭代法
from collections import deque

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

心得收获

本题最大的误区就是处理逻辑和最大深度一样,如果一个节点的左孩子或者右孩子为空,此时的最小深度并不为0,而是要看有节点的一侧最小的深度是多少。

;