ACM 选手图解 LeetCode 对称二叉树

简介: ACM 选手图解 LeetCode 对称二叉树

大家好呀,我是对称蛋。


今天解决对称二叉树,比较有意思的经典题目,检查二叉树是否是镜像对称。


不比比,直接开搞。

640.png


   LeetCode 101:对称二叉树



题意


给定一个二叉树,检查它是否是镜像对称的。


示例


二叉树 [1,2,2,3,4,4,3] 镜像对称。

640.png


二叉树 [1,2,3,null,3,null,3] 非镜像对称

640.png


提示


  • 爱帅蛋
  • 么么哒


   题目解析



题目经典,难度简单。


这道题又是说对称,又是说镜像的,看着挺唬人,其实就是看左右子树是否相互翻转。

640.png


再直白点就是比较两棵树(在这道题里,根节点没什么用,把根节点拿掉来看,就是两棵树)。

640.png



那怎么比较呢?


我来的颜色稍作修,来看下图:

640.png


是不是就很一目了然?


左子树的左节点和右子树的右节点,左子树的右节点和右子树的左节点比较即可。


如果一棵树的左子树的左子树和右子树的右子树相等,左子树的右子树和右子树的左子树相等,那这棵树就是对称的。

640.jpg


递归法


还是老套路,根据【递归算法】文章中讲的,实现递归,需要两步:


  • 找出重复的子问题(递推公式)。
  • 终止条件。


(1) 找出重复的子问题。


这个在上面看图的时候其实已经找出来了。


对于每一层来说,比较的都是左子树的左节点和右子树的右节点,左子树的右节点和右子树的左节点是否相等


如果相等就返回 true,不相等就返回 false。


# 使用递归,对接下来的每一层做判断
# 判断左子树的左子树和右子树的右子树是否相等
leftrightTree = self.compareTree(left.left, right.right)
# 判断左子树的右子树和右子树的左子树是否相等
rightleftTree = self.compareTree(left.right, right.left)
isCompareTree = leftrightTree and rightleftTree  


(2) 确定终止条件。


对称二叉树这道题因为比较的是节点的值是否相同,所以涉及的情况有点多。


首先是节点为空的情况,这种必须首先考虑,不然会出现空指针的比较。


节点为空,分为 3 种情况:


  • 左右节点都为空,此时相当于只有个头节点,是对称的。
  • 左节点为空,右节点不为空,显然不对称。
  • 左节点不为空,右节点为空,显然也是不对称的。


节点为空的情况判断完了,剩下就是节点不为空的情况,节点不为空,其实终止就 1 种情况:


  • 比较两个节点的值,如果不相等,则不对称。
# 若左右节点不存在(即只有根节点)
if left == None and right == None:
    return True
# 左节点为空右节点不为空或左节点不为空右节点为空,则不对称
elif (left == None and right != None) or (left != None and right == None):
    return False
# 左右节点不为空,但数值不等,则不对称
elif left.val != right.val:
    return False


递归二步曲确定完了,这道题其实也就做出来了。


Python 代码实现

# 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 compareTree(self, left, right):
        # 若左右节点不存在(即只有根节点)
        if left == None and right == None:
            return True
        # 左节点为空右节点不为空或左节点不为空右节点为空,则不对称
        elif (left == None and right != None) or (left != None and right == None):
            return False
        # 左右节点不为空,但数值不等,则不对称
        elif left.val != right.val:
            return False
        # 使用递归,对接下来的每一层做判断
        # 判断左子树的左子树和右子树的右子树是否相等
        leftrightTree = self.compareTree(left.left, right.right)
        # 判断左子树的右子树和右子树的左子树是否相等
        rightleftTree = self.compareTree(left.right, right.left)
        isCompareTree = leftrightTree and rightleftTree 
        return isCompareTree
    def isSymmetric(self, root: TreeNode) -> bool:
        # 空树
        if root == None:
            return True
        return self.compareTree(root.left, root.right)


Java 代码实现

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
  public boolean isSymmetric(TreeNode root) {
    if(root == null) {
      return true;
    }
    return compareTree(root.left,root.right);
  }
  boolean compareTree(TreeNode left, TreeNode right) {
        // 若左右节点不存在(即只有根节点)
    if(left == null && right == null) {
      return true;
    }
        // 左节点为空右节点不为空或左节点不为空右节点为空,则不对称
    if((left == null && right != null) || (left != null && right == null)) {
      return false;
    }
        // 左右节点不为空,但数值不等,则不对称
    if(left.val!=right.val) {
      return false;
    }
        // 判断左子树的左子树和右子树的右子树是否相等
        boolean leftrightTree = compareTree(left.left,right.right);
        // 判断左子树的右子树和右子树的左子树是否相等
        boolean rightleftTree = compareTree(left.right,right.left);
        boolean isCompareTree = leftrightTree && rightleftTree;
        return isCompareTree;
  }
}


此解法,由于每个节点被遍历一次,所以时间复杂度为 O(n)


使用递归,在过程中额外调用了栈空间,所以空间复杂度为 O(n)


非递归法(迭代)


在递归法法中我说过:对于每一层来说,比较的都是左子树的左节点和右子树的右节点,左子树的右节点和右子树的左节点是否相等。


其实这有点类似层次遍历。


每次将当前层的节点,按照左子树的左节点、右子树的右节点、左子树的右节点和右子树的左节点放入队列,再依次两两出队列,比较数值是否相等。


比如对于下图:

640.png


首先初始化队列,并将根节点的左右节点入队列。

640.png

# 初始化队列
queue = [root.left, root.right]

当队列不为空,将前两个元素出队列进行比较。

640.png

# 从队列中取出两个节点
left = queue.pop(0)
right = queue.pop(0)
# 若两个节点都为空(左右节点不存在),则继续循环
if left == None and right == None:
    continue
# 左节点为空右节点不为空或左节点不为空右节点为空,则不对称
elif (left == None and right != None) or (left != None and right == None):
    return False
# 左右节点不为空,但数值不等,则不对称
elif left.val != right.val:
    return False


下面再依次将左子树的左节点、右子树的右节点、左子树的右节点和右子树的左节点放入队列。

640.png


接下来还是按照上面的方式出队列、比较、入队列...直至队列为空。


Python 代码实现

# 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
        # 初始化队列
        queue = [root.left, root.right]
        while queue:
            # 从队列中取出两个节点
            left = queue.pop(0)
            right = queue.pop(0)
            # 若两个节点都为空(左右节点不存在),则继续循环
            if left == None and right == None:
                continue
            # 左节点为空右节点不为空或左节点不为空右节点为空,则不对称
            elif (left == None and right != None) or (left != None and right == None):
                return False
            # 左右节点不为空,但数值不等,则不对称
            elif left.val != right.val:
                return False
            # 左节点的左孩子和右节点的右孩子入队列
            queue.append(left.left)
            queue.append(right.right)
            # 左节点的右孩子和右节点的左孩子入队列
            queue.append(left.right)
            queue.append(right.left)
        return True

Java 代码实现


/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
  public boolean isSymmetric(TreeNode root) {
    if(root==null) {
      return true;
    }
    // 初始化队列
    LinkedList<TreeNode> queue = new LinkedList<TreeNode>();
    queue.add(root.left);
    queue.add(root.right);
    while(queue.size() > 0) {
      // 从队列中取出两个节点
      TreeNode left = queue.removeFirst();
      TreeNode right = queue.removeFirst();
      // 若两个节点都为空(左右节点不存在),则继续循环
      if(left == null && right == null) {
        continue;
      }
            // 左节点为空右节点不为空或左节点不为空右节点为空,则不对称
      if((left == null && right != null) || (left != null && right == null)){
        return false;
      }
            // 左右节点不为空,但数值不等,则不对称
      if(left.val != right.val) {
        return false;
      }
      // 左节点的左孩子和右节点的右孩子入队列
      queue.add(left.left);
      queue.add(right.right);
      // 左节点的右孩子和右节点的左孩子入队列
      queue.add(left.right);
      queue.add(right.left);
    }
    return true;
  }
}

同样,非递归法,时间复杂度为 O(n),空间复杂度为 O(n)


图解对称二叉树到这就结束辣,大噶伙儿学废了嘛?


是不是感觉还挺简单的,我一直觉得,二叉树的题目还蛮有意思的。


一道题,拆着拆着就拆成了自己认识的样子。


二叉树这个专题,我会带大家多做一些实战题,争取学完就搞懂。


大家加油,当然也要记得给本蛋点赞 +  在看 + 转发,给我加加油!


我是帅蛋,我们下次见辣!

相关文章
|
12月前
|
Go 开发者 索引
【LeetCode 热题100】路径与祖先:二叉树中的深度追踪技巧(力扣33 / 81/ 153/154)(Go语言版)
本文深入探讨了LeetCode中四道关于「搜索旋转排序数组」的经典题目,涵盖了无重复和有重复元素的情况。通过二分查找的变形应用,文章详细解析了每道题的解题思路和Go语言实现代码。关键点包括判断有序区间、处理重复元素以及如何缩小搜索范围。文章还总结了各题的异同,并推荐了类似题目,帮助读者全面掌握二分查找在旋转数组中的应用。无论是初学者还是有经验的开发者,都能从中获得实用的解题技巧和代码实现方法。
490 14
|
Python
【Leetcode刷题Python】剑指 Offer 32 - III. 从上到下打印二叉树 III
本文介绍了两种Python实现方法,用于按照之字形顺序打印二叉树的层次遍历结果,实现了在奇数层正序、偶数层反序打印节点的功能。
223 6
|
算法 Go
【LeetCode 热题100】深入理解二叉树结构变化与路径特性(力扣104 / 226 / 114 / 543)(Go语言版)
本博客深入探讨二叉树的深度计算、结构变换与路径分析,涵盖四道经典题目:104(最大深度)、226(翻转二叉树)、114(展开为链表)和543(二叉树直径)。通过递归与遍历策略(前序、后序等),解析每题的核心思路与实现方法。结合代码示例(Go语言),帮助读者掌握二叉树相关算法的精髓。下一讲将聚焦二叉树构造问题,欢迎持续关注!
366 10
|
存储 算法 数据可视化
【二叉树遍历入门:从中序遍历到层序与右视图】【LeetCode 热题100】94:二叉树的中序遍历、102:二叉树的层序遍历、199:二叉树的右视图(详细解析)(Go语言版)
本文详细解析了二叉树的三种经典遍历方式:中序遍历(94题)、层序遍历(102题)和右视图(199题)。通过递归与迭代实现中序遍历,深入理解深度优先搜索(DFS);借助队列完成层序遍历和右视图,掌握广度优先搜索(BFS)。文章对比DFS与BFS的思维方式,总结不同遍历的应用场景,为后续构造树结构奠定基础。
605 10
|
Go 索引 Perl
【LeetCode 热题100】【二叉树构造题精讲:前序 + 中序建树 & 有序数组构造 BST】(详细解析)(Go语言版)
本文详细解析了二叉树构造的两类经典问题:通过前序与中序遍历重建二叉树(LeetCode 105),以及将有序数组转化为平衡二叉搜索树(BST,LeetCode 108)。文章从核心思路、递归解法到实现细节逐一拆解,强调通过索引控制子树范围以优化性能,并对比两题的不同构造逻辑。最后总结通用构造套路,提供进阶思考方向,帮助彻底掌握二叉树构造类题目。
830 9
|
Go
【LeetCode 热题100】路径与祖先:二叉树中的深度追踪技巧(力扣437 / 236 )(Go语言版)
本文深入探讨二叉树中路径与祖先问题,涵盖两道经典题目:LeetCode 437(路径总和 III)和236(最近公共祖先)。对于路径总和 III,文章分析了双递归暴力解法与前缀和优化方法,后者通过哈希表记录路径和,将时间复杂度从O(n²)降至O(n)。在最近公共祖先问题中,采用后序遍历递归查找,利用“自底向上”的思路确定最近公共祖先节点。文中详细解析代码实现与核心要点,帮助读者掌握深度追踪技巧,理解树结构中路径与节点关系的本质。这类问题在面试中高频出现,掌握其解法意义重大。
300 4
|
存储 算法
二叉树进阶-学会层序遍历助你一次刷完leetcode10道题
文章深入探讨了二叉树的层序遍历方法,并展示了如何通过队列实现层序遍历的算法逻辑,同时指出掌握层序遍历技巧可以帮助解决LeetCode上的多道相关题目。
二叉树进阶-学会层序遍历助你一次刷完leetcode10道题
【LeetCode 31】104.二叉树的最大深度
【LeetCode 31】104.二叉树的最大深度
111 2
【LeetCode 29】226.反转二叉树
【LeetCode 29】226.反转二叉树
129 2
【LeetCode 28】102.二叉树的层序遍历
【LeetCode 28】102.二叉树的层序遍历
135 2