news 2026/3/31 5:54:51

LeetCode 100/101/110/199 对称/平衡二叉树与右视图

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
LeetCode 100/101/110/199 对称/平衡二叉树与右视图

目录

一、100. 相同的树

问题描述

核心思想:递归分治

实现方法

重点 & 难点

二、101. 对称二叉树:“镜像问题”

问题描述

核心思想

实现方法

方法 1:递归(辅助函数比较两棵子树)

方法 2:迭代(队列成对存储镜像节点)

重点 & 难点

三、110. 平衡二叉树:后序遍历的 “剪枝”

问题描述

核心思想

实现方法(优化版:后序遍历 + 剪枝)

重点 & 难点

四、199. 二叉树的右视图:遍历顺序的 “状态控制”

问题描述

核心思想:层序遍历 / 优先右子树的 DFS

实现方法

方法 1:层序遍历(直观易理解)

方法 2:DFS(优先右子树)

重点 & 难点

总结:二叉树问题的通用解题逻辑

1. 递归分治:拆解子问题

2. 遍历控制:记录状态

模板使用总结


本文会拆解 LeetCode 中 4 道二叉树高频题 ——100. 相同的树、101. 对称二叉树、110. 平衡二叉树、199. 二叉树的右视图,从核心思想、实现方法到重点难点做深入分析。

一、100. 相同的树

问题描述

给定两棵二叉树的根节点pq,判断二者是否结构相同且对应节点值相等

核心思想:递归分治

树的递归结构决定了 “整棵树相同” 可以拆解为 3 个子问题:

  1. 当前节点的值相等
  2. p的左子树与q的左子树相同;
  3. p的右子树与q的右子树相同。

实现方法

递归的关键是处理边界条件(空节点是二叉树递归的 “必考题”):

碰到递归问题,先从边界条件出发,慢慢推导

class Solution { public boolean isSameTree(TreeNode p, TreeNode q) { // 边界1:两棵树都空,相同 if (p == null && q == null) return true; // 边界2:其中一棵空、另一棵非空,不同 if (p == null || q == null) return false; // 子问题:值相等 + 左子树相同 + 右子树相同 return p.val == q.val && isSameTree(p.left, q.left) && isSameTree(p.right, q.right); } }

重点 & 难点

  • 重点:递归的 “分治拆解”—— 把整树问题拆成 “当前节点 + 左右子树” 的子问题;
  • 难点:容易忽略 “一个空、一个非空” 的边界条件,导致空指针异常或逻辑错误。

二、101. 对称二叉树:“镜像问题”

问题描述

给定一棵二叉树的根节点root,判断其是否是镜像对称的(即左右子树呈镜像结构)。

核心思想:转化为 “两棵树的镜像比较”

“一棵树对称” 等价于 “它的左子树和右子树互为镜像”,而 “两棵树镜像” 的子问题是:

  1. 树 A 的根节点值 = 树 B 的根节点值;
  2. 树 A 的左子树 与 树 B 的右子树 镜像;
  3. 树 A 的右子树 与 树 B 的左子树 镜像。

实现方法

方法 1:递归(辅助函数比较两棵子树)
class Solution { public boolean isSymmetric(TreeNode root) { // 空树是对称的;否则比较左、右子树是否镜像 return root == null ? true : isMirror(root.left, root.right); } // 辅助函数:判断两棵树是否镜像 private boolean isMirror(TreeNode a, TreeNode b) { if (a == null && b == null) return true; if (a == null || b == null) return false; // 子问题:值相等 + 左对右、右对左 return a.val == b.val && isMirror(a.left, b.right) && isMirror(a.right, b.left); } }
方法 2:迭代(队列成对存储镜像节点)

用队列将 “需要比较的镜像节点” 成对入队,每次取出一对节点校验:

public boolean isSymmetric(TreeNode root) { if (root == null) return true; Queue<TreeNode> queue = new LinkedList<>(); queue.offer(root.left); queue.offer(root.right); while (!queue.isEmpty()) { TreeNode a = queue.poll(); TreeNode b = queue.poll(); // 处理空节点的情况 if (a == null && b == null) continue; if (a == null || b == null) return false; if (a.val != b.val) return false; // 成对入队镜像节点:a左对b右,a右对b左 queue.offer(a.left); queue.offer(b.right); queue.offer(a.right); queue.offer(b.left); } return true; }

重点 & 难点

  • 重点:问题转化 —— 将 “一棵树的对称” 转化为 “两棵子树的镜像比较”,是递归的关键;
  • 难点:镜像节点的对应关系(左对右、右对左)容易搞反,导致逻辑错误。

三、110. 平衡二叉树:后序遍历的 “剪枝”

问题描述

判断一棵二叉树是否是平衡二叉树(AVL 树):每个节点的左右子树高度差的绝对值不超过 1。

核心思想:后序遍历 + 剪枝

平衡二叉树需要先获取子树高度,再判断当前节点的平衡度—— 后序遍历(左→右→根)恰好符合 “先处理子树、再处理根” 的需求。

同时,为了避免重复计算子树高度(基础版会重复计算),我们可以在计算高度的同时集成平衡判断:若某子树不平衡,直接返回标记值(如-1),提前终止递归(剪枝)。

实现方法(优化版:后序遍历 + 剪枝)

class Solution { public boolean isBalanced(TreeNode root) { // 若返回-1则不平衡,否则平衡 return getHeightAndCheck(root) != -1; } // 辅助函数:计算高度 + 检查平衡,不平衡返回-1 private int getHeightAndCheck(TreeNode node) { if (node == null) return 0; // 空节点高度为0 // 先检查左子树 int leftHeight = getHeightAndCheck(node.left); if (leftHeight == -1) return -1; // 再检查右子树 int rightHeight = getHeightAndCheck(node.right); if (rightHeight == -1) return -1; // 检查当前节点的平衡因子 if (Math.abs(leftHeight - rightHeight) > 1) return -1; // 平衡则返回当前节点高度 return Math.max(leftHeight, rightHeight) + 1; } }

重点 & 难点

  • 重点:后序遍历的应用场景(需要子树信息的问题)、剪枝技巧(避免无效计算);
  • 难点:将 “高度计算” 与 “平衡判断” 合并,需要清晰的递归返回值逻辑(高度 / 不平衡标记)。

四、199. 二叉树的右视图:遍历顺序的 “状态控制”

问题描述

从二叉树的右侧观察,返回从顶部到底部能看到的节点值(即每一层的最后一个节点)。

核心思想:层序遍历 / 优先右子树的 DFS

  • 层序遍历(BFS):记录每一层的节点数,遍历完一层后,取最后一个节点;
  • 深度优先遍历(DFS):优先访问右子树,记录当前深度,当深度等于结果列表长度时,说明是该层的 “第一个可见节点”(因先右,所以是最右侧)。

实现方法

方法 1:层序遍历(直观易理解)
class Solution { public List<Integer> rightSideView(TreeNode root) { List<Integer> result = new ArrayList<>(); if (root == null) return result; Queue<TreeNode> queue = new LinkedList<>(); queue.offer(root); while (!queue.isEmpty()) { int levelSize = queue.size(); // 当前层的节点数 for (int i = 0; i < levelSize; i++) { TreeNode node = queue.poll(); // 若为当前层最后一个节点,加入结果 if (i == levelSize - 1) { result.add(node.val); } // 左、右子节点入队(保证下一层的顺序) if (node.left != null) queue.offer(node.left); if (node.right != null) queue.offer(node.right); } } return result; } }
方法 2:DFS(优先右子树)
class Solution { public List<Integer> rightSideView(TreeNode root) { List<Integer> result = new ArrayList<>(); dfs(root, 0, result); return result; } private void dfs(TreeNode node, int depth, List<Integer> result) { if (node == null) return; // 深度 == 结果长度 → 该层第一个节点(先右,故为最右侧) if (depth == result.size()) { result.add(node.val); } // 优先访问右子树,深度+1 dfs(node.right, depth + 1, result); dfs(node.left, depth + 1, result); } }

重点 & 难点

  • 重点:通过 “层大小”(BFS)或 “深度”(DFS)控制 “每一层的可见节点”;
  • 难点:DFS 中 “深度与结果列表长度的对应关系”—— 需要理解 “先右子树” 带来的层节点顺序。

总结:二叉树问题的通用解题逻辑

这 4 道题覆盖了二叉树的核心解题思路,总结为 2 个关键词:

1. 递归分治:拆解子问题

二叉树的递归问题,核心是找到 “整树→子树” 的拆解逻辑,同时必须处理空节点的边界条件:

  • 相同的树:整树相同 → 节点值 + 左子树 + 右子树相同;
  • 对称二叉树:整树对称 → 左子树与右子树镜像。

2. 遍历控制:记录状态

需要按顺序访问节点或记录层 / 深度信息时,用遍历(BFS/DFS)+ 状态变量(层大小、深度):

  • 平衡二叉树:后序遍历(先子树后根)+ 高度状态;
  • 右视图:层序遍历(层大小)/ DFS(深度)+ 可见节点状态。

模板使用总结

模板类型核心适用场景关键技巧
递归分治整树→子树拆解、需子树信息优先处理空节点、剪枝(提前终止)
BFS 层序按层处理、找层内特征记录每层节点数、控制层边界
DFS 遍历路径记录、深度相关、遍历顺序状态变量传递(深度 / 路径)、调整遍历顺序

快速选型技巧

  1. 看到 “平衡、相同、对称、最大深度”→ 用递归分治模板
  2. 看到 “按层、右视图、左下角、层最大值”→ 用BFS 模板
  3. 看到 “前 / 中 / 后序遍历、路径、深度优先”→ 用DFS 模板

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/3/29 17:01:24

探索Libertinus字体:专业排版的全能解决方案

探索Libertinus字体&#xff1a;专业排版的全能解决方案 【免费下载链接】libertinus The Libertinus font family 项目地址: https://gitcode.com/gh_mirrors/li/libertinus 想要找到一款既美观又实用的开源字体吗&#xff1f;&#x1f3a8; Libertinus字体家族正是你需…

作者头像 李华
网站建设 2026/3/14 12:57:20

一文搞懂Python迭代器和生成器

很多童鞋搞不懂python迭代器和生成器到底是什么&#xff1f;它们之间又有什么样的关系&#xff1f;这篇文章就是要用最简单的方式让你理解Python迭代器和生成器&#xff01;迭代器和迭代过程维基百科解释道&#xff1a;在Python中&#xff0c;迭代器是遵循迭代协议的对象。使用…

作者头像 李华
网站建设 2026/3/28 22:40:46

Vue虚拟滚动列表终极指南:告别大数据量卡顿的完整解决方案

Vue虚拟滚动列表终极指南&#xff1a;告别大数据量卡顿的完整解决方案 【免费下载链接】vue-virtual-scroll-list ⚡️A vue component support big amount data list with high render performance and efficient. 项目地址: https://gitcode.com/gh_mirrors/vu/vue-virtual…

作者头像 李华
网站建设 2026/3/16 16:03:08

传统VS现代:乱码生成效率提升10倍的方法

快速体验 打开 InsCode(快马)平台 https://www.inscode.net输入框内输入如下内容&#xff1a; 构建一个高效的乱码生成API服务&#xff0c;要求&#xff1a;1.支持RESTful接口 2.能处理高并发请求 3.提供多种乱码生成算法 4.有请求频率限制 5.返回JSON格式结果。使用Go语言开发…

作者头像 李华
网站建设 2026/3/30 22:00:27

PHP+OpenSSL实现银行级支付加密(实战代码+性能优化策略)

第一章&#xff1a;银行级支付加密的背景与PHP技术选型 在金融系统日益数字化的今天&#xff0c;支付安全成为银行与第三方支付平台的核心关注点。数据泄露、中间人攻击和身份伪造等风险促使企业必须采用银行级加密标准&#xff0c;如TLS 1.3、AES-256-GCM 和 RSA-OAEP&#xf…

作者头像 李华