leetcode—18.二叉树性质相关题目leetcode总结

本文详细介绍了LeetCode中涉及二叉树性质的题目,包括100. 相同的树、101. 对称二叉树、110. 平衡二叉树等,通过递归和迭代两种方法解析解题思路,帮助读者理解二叉树的性质及其应用。
摘要由CSDN通过智能技术生成

100. 相同的树

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

示例:

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

思路1:递归

终止条件:p节点和q节点都为空或者两者之一为空,或者两者值不等。再次比较p节点和q节点的左孩子,以及比较p节点和q节点的右孩子

# 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 isSameTree(self, p: TreeNode, q: TreeNode) -> bool:
        """
        递归停止条件::p节点和q节点都为空或者两者之一为空,或者两者值不等
        """
        if p == None and q == None:
            return True
        
        if p == None or q == None:
            return False

        if p.val != q.val:
            return False

        # 比较p和q的左孩子与右孩子,如果左孩子不相等了就不需要比较右孩子,短路效应
        return self.isSameTree(p.left,q.left) and self.isSameTree(p.right,q.right)

思路2:迭代

  1. 首先用一个栈来保存根节点p,q。接着不断遍历这个栈。
  2. 我们从栈中拿出两个元素进行比较,如果这两个元素不等(一个是空一个不为空,或者两个节点的值不等),就直接返回false。如果这两个节点的值相等,就继续把 p 节点的左孩子,q节点的左孩子放入栈中;再把p节点的右孩子,q节点的右孩子放入栈中。重复这个步骤,直到栈为空。
  3. 如果整个循环遍历完了,说明两个树的元素都是相等的,返回 true。
# 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 isSameTree(self, p: TreeNode, q: TreeNode) -> bool:
        """
        迭代解决
        """
        # 用栈来保存p和q遍历时的节点
        stack = [(p,q)]
        while stack:
            a,b = stack.pop()
            # 如果a,b为空,则继续下一轮比较
            if a == None and b == None:
                continue

            if a and b and a.val == b.val:
                stack.append((a.left,b.left))
                stack.append((a.right,b.right))
            else:
                return False
        # 循环结束时,stack为空
        return True

101. 对称二叉树

给定一个二叉树,检查它是否是镜像对称的。
例如,二叉树 [1,2,2,3,4,4,3] 是对称的

但是下面这个 [1,2,2,null,3,null,3] 则不是镜像对称的:
在这里插入图片描述

思路:

如果一个树的左子树与右子树镜像对称,那么这个树是对称的。因此,该问题可以转化为:两个树在什么情况下互为镜像?
如果同时满足下面的条件,两个树互为镜像:

  • 它们的两个根结点具有相同的值。
  • 每个树的右子树都与另一个树的左子树镜像对称。

如何去判断二叉树是镜像对称?

  1. 根节点为null,直接返回True;
  2. 当根节点不为null时,判断左右子树是否对称:判断root.left跟root.right节点值是否相同
  3. 判断root.left的左子树是否与root.right的右子树对称,判断root.left的右子树是否与root.right的左子树对称

终止条件:left 和right不等,或者left 和right都为空

# 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 isSymmetric(self, root: TreeNode) -> bool:
        if root == None:
            return True
        
        def dfs(left,right):
            """
            终止条件:left 和right不等,或者left 和right都为空
            """
            if left == None and right == None:
                return True
            if left == None or right == None:
                return False
            if left.val != right.val:
                return False

            # 单个镜像不对称时,则镜像非对称
            return dfs(left.left,right.right) and dfs(left.right,right.left)
        # 递归比较左右子树
        return dfs(root.left,root.right)

思路2:迭代

这里使用一个队列

  1. 首先从队列中拿出两个节点(left 和right)比较
  2. 将left 的left节点和right 的right 节点放入队列,将left 的right节点和right 的left节点放入队列,然后进行迭代
# 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 isSymmetric(self, root: TreeNode) -> bool:
        # 使用队列-先进先出
        if root == None:
            return True
        
        q = [(root.left,root.right)]
        while q != []:
            left,right = q.pop(0)
            if left == None and right == None:
                continue
            
            if left and right and left.val == right.val:
                # 将左右孩子添加入队列
                q.append((left.left,right.right))
                q.append((left.right,right.left))
            else:
                return False
        
        return True

在这里插入图片描述

思路3:

这里使用一个栈

  1. 首先从队列中拿出两个节点(left 和right)比较
  2. 将left 的left节点和right 的right 节点放入队列,将left 的right节点和right 的left节点放入栈,然后进行迭代
# 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 isSymmetric(self, root: TreeNode) -> bool:
        # 使用栈-后进先出
        if root == None:
            return True
        
        stack = [(root.left,root.right)]
        while stack != []:
            left,right = stack.pop()
            if left == None and right == None:
                continue
            
            if left and right and left.val == right.val:
                # 将左右孩子添加入栈
                stack.append((left.left,right.right))
                stack.append((left.right,right.left))
            else:
                return False
        
        return True

在这里插入图片描述

110. 平衡二叉树

给定一个二叉树,判断它是否是高度平衡的二叉树。
本题中,一棵高度平衡二叉树定义为:
一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1 。

示例:

输入:root = [3,9,20,null,null,15,7]
输出:true

在这里插入图片描述
思路1:自顶向下暴力递归

构造一个获取当前节点最大深度的方法depth(),通过比较左右子树最大高度差abs(self.depth(root.left) - self.depth(root.right)),来判断以此节点为根节点下是否是二叉平衡树;

  • 从顶至底深度优先遍历DFS,以每个节点为根节点,递归判断是否是平衡二叉树
    若所有根节点都满足平衡二叉树性质,则返回True ;
    若其中任何一个节点作为根节点时,不满足平衡二叉树性质,则返回 False。
    本方法产生大量重复的节点访问和计算,最差情况下时间复杂度O(N^2)。
# 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 isBalanced(self, root: TreeNode) -> bool:
        # 自顶向下暴力递归
        # 递归判断每一个子树是否是平衡二叉树
        # 递归计算每一个二叉树的深度
        
        if root == None:
            return True
        
        if abs(self.depth(root.left) - self.depth(root.right)) <= 1:
            # 递归判断左右子树是否是平衡树
            return self.isBalanced(root.left) and self.isBalanced(root.right)
        # 递归判断是否是平衡二叉树的终止条件:高度差大于1
        else:
            return False

    # 计算二叉树的深度
    def depth(self,root):
        # 终止条件为根节点为空或者只有根节点
        if root == None:
            return 0
        if root.left == None and root.right == None:
            return 1
        # 有一个节点不为空时,递归计算其深度
        if root.left != None or root.right != None:
            return max(self.depth(root.left),self.depth(root.right)) + 1

思路2:自底向上(提前阻断)

对二叉树做深度优先遍历DFS,
终止条件:当DFS 越过叶子节点时,返回高度O;

返回值:
从底至顶,返回以每个节点root为根节点的子树最大高度(左右子树中最大的高度值加1,max(left,right)+1))

当我们发现有一例左/右子树高度差>1的情况时,代表此树不是平衡树,返回-1
当发现不是平衡树时,后面的高度计算都没有意义了,因此一路返回– 1,避免后续多余计算。
最差情况是对树做一遍完整DFS,时间复杂度为O(N)。

# 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 isBalanced(self, root: TreeNode) -> bool:
        """
        递归+提前阻断
        """
        if self.depth(root) == -1:
            return False
        else:
            return True

    def depth(self,root):
        if root == None:
            return 0
        depth_left = self.depth(root.left)
        # 提前阻断
        if depth_left == -1:
            return -1
        
        depth_right = self.depth(root.right)
        # 提前阻断
        if depth_right == -1:
            return -1

        if abs(depth_left - depth_right) <= 1:
            return 1 + max(depth_left,depth_right)
        # 提前阻断
        else:
            return -1

104. 二叉树的最大深度

给定一个二叉树,找出其最大深度。
二叉树的深度为根节点到最远叶子节点的最长路径上的节点数。
说明: 叶子节点是指没有子节点的节点。

示例:

给定二叉树 [3,9,20,null,null,15,7]
在这里插入图片描述
返回它的最大深度 3 。

思路1:迭代

迭代实现其实就是广度遍历,遍历每一层的节点高度,然后求得最深的一个节点的高度,就是整个树的高度了。

# 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 maxDepth(self, root: TreeNode) -> int:
        """
        利用栈来迭代遍历
        """
        if root == None:
            return 0
        
        stack = [(1,root)]
        depth = 0

        while stack:
            # 从栈中弹出元素
            cur_depth,node = stack.pop()
            if node:
                # 比较当前深度与depth,并记录当前最大深度
                depth = max(cur_depth,depth)
                # 将左节点放入栈
                stack.append((cur_depth+1,node.left))
                # 将右节点放入栈
                stack.append((cur_depth+1,node.right))
        return depth

思路2:递归

递归终止条件:当节点为空时返回
再次递归计算max(左节点最大高度,右节点最大高度)+1。+1相当于把根节点也算上

# 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 maxDepth(self, root: TreeNode) -> int:
        """递归"""
        # 如果节点为空,那么深度为0
        if root == None:
            return 0
        # 继续递归左子树与右子树,最后算深度时,把根节点算上
        return max(self.maxDepth(root.left),self.maxDepth(root.right)) + 1

111. 二叉树的最小深度

给定一个二叉树,找出其最小深度。
最小深度是从根节点到最近叶子节点的最短路径上的节点数量。
说明:叶子节点是指没有子节点的节点。

示例:

输入:root = [3,9,20,null,null,15,7]
输出:2

思路1:迭代

层次遍历二叉树,如果树为空,直接返回0。否则将树和深度值1入队列,逐一弹出队列中节点:
若某节点左右子树均为空,此节点即为叶子节点,我们将它的深度与最小深度min_depth进行比较,更新最小深度。
若其存在子树,则将其存在的子树和子树深度入队列。
实际上,因为层次遍历是一层一层遍历的,所以第一个叶子节点即为最小深度的叶子节点,直接返回其深度即可。这样就不用遍历所有的节点。

# 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 minDepth(self, root: TreeNode) -> int:
        """使用队列保存"""
        if root == None:
            return 0

        queue = [(root,1)]
        while queue:
            # 先从队列中弹出元素
            node,depth = queue.pop(0)
            # 当叶子节点出现时,不在继续操作
            if node.left == None and node.right == None:
                return depth
            
            # 左节点不为空,则将左节点加入队列
            if node.left != None:
                queue.append((node.left,depth+1))
            if node.right != None:
                queue.append((node.right,depth+1))

思路2:递归

递归解法的关键是搞清楚递归结束条件:
当root 节点左右孩子都为空(叶子节点)时,返回1
当root节点左右孩子有一个为空时,返回不为空的孩子节点的深度
当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 minDepth(self, root: TreeNode) -> int:
        """递归"""
        if root == None:
            return 0
        # 当root 节点左右孩子都为空(叶子节点)时,返回1
        if root.left == None and root.right == None:
            return 1
		# 当root节点左右孩子有一个为空时,返回不为空的孩子节点的深度
        if root.left == None or root.right == None:
            return max(self.minDepth(root.left),self.minDepth(root.right)) + 1
        # 当root节点左右孩子都不为空时,返回左右孩子较小深度的节点值
        if root.left != None and root.right != None:
            return min(self.minDepth(root.left),self.minDepth(root.right)) +1

662. 二叉树最大宽度

给定一个二叉树,编写一个函数来获取这个树的最大宽度。树的宽度是所有层中的最大宽度。这个二叉树与满二叉树(full binary tree)结构相同,但一些节点为空。
每一层的宽度被定义为两个端点(该层最左和最右的非空节点,两端点间的null节点也计入长度)之间的长度。

示例:

输入:

      1
    /   \
    3     2
   / \     \  
  5   3     9 

输出: 4
解释: 最大值出现在树的第 3 层,宽度为 4 (5,3,null,9)。

思路:迭代层次遍历

因为两端点间的None值也计入,所以这里我们不能简单的统计每一层的节点数,这里我们可以考虑给树中的每一个节点进行编号,根节点为1,然后如果是左节点,值为根节点的二倍;如果是右节点,值为根节点的二倍加一。
这里用层次遍历,采用一个队列记录每一个节点的节点以及号码,一层的首末元素的编号差就是该层的最大宽度。

# 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 widthOfBinaryTree(self, root: TreeNode) -> int:
        # 判断根节点是否为空
        if root == None:
            return 0
        
        queue = [(root,1)]
        width = 0
        # 层次遍历,每一层whie判断一次
        while queue:
            length = len(queue)
            for i in range(length):
                node,nums = queue.pop(0)
                # 分别记录每一层第一个节点的编号与最后一个节点的编号
                if i == 0:
                    first_num = nums
                if i == length-1:
                    last_num = nums
                # 更新宽度
                width = max(width,last_num - first_num + 1)
                    

                # 左右子树入队
                if node.left != None:
                    queue.append((node.left,2*nums))
                if node.right != None:
                    queue.append((node.right,2*nums+1))
        
        return width

如果对您有帮助,麻烦点赞关注,这真的对我很重要!!!如果需要互关,请评论或者私信!
在这里插入图片描述


评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值