二叉树leetcodeJAVA2
226.翻转二叉树
给你一棵二叉树的根节点
root
,翻转这棵二叉树,并返回其根节点。示例 1:
输入:root = [4,2,7,1,3,6,9] 输出:[4,7,2,9,6,3,1]示例 2:
输入:root = [2,1,3] 输出:[2,3,1]示例 3:
输入:root = [] 输出:[]提示:
- 树中节点数目范围在
[0, 100]
内-100 <= Node.val <= 100
解题思路
翻转二叉树的核心在于递归地交换每个节点的左右子节点。从根节点开始,先将其左子树和右子树交换(使用临时变量保存其中一个子树),然后递归地对新的左子树和右子树执行相同的操作,直到遇到空节点(null)停止。整个过程通过自顶向下的递归,确保每个节点的左右子树都被正确翻转,最终返回调整后的根节点。
代码
class Solution {
public TreeNode invertTree(TreeNode root) {
// 如果根节点为空,直接返回 null(空树无需翻转)
if (root == null) {
return root;
}
// 创建一个临时节点,用于交换左右子节点
TreeNode temp = new TreeNode();
// 将左子节点赋值给临时变量
temp = root.left;
// 将右子节点赋值给左子节点
root.left = root.right;
// 将临时变量(原来的左子节点)赋值给右子节点
root.right = temp;
// 递归翻转左子树
invertTree(root.left);
// 递归翻转右子树
invertTree(root.right);
// 返回翻转后的根节点
return root;
}
}
101.对称二叉树
给你一个二叉树的根节点
root
, 检查它是否轴对称。示例 1:
输入:root = [1,2,2,3,4,4,3] 输出:true示例 2:
输入:root = [1,2,2,null,3,null,3] 输出:false提示:
- 树中节点数目在范围
[1, 1000]
内-100 <= Node.val <= 100
进阶:你可以运用递归和迭代两种方法解决这个问题吗?
解题思路1
检查二叉树是否轴对称的核心在于递归比较左右子树的镜像对称性。从根节点的左右子树开始,判断它们是否满足以下条件:两节点要么都为空,要么都不为空且值相等,同时左子树的左子节点与右子树的右子节点对称,左子树的右子节点与右子树的左子节点对称。这个过程通过递归深入每一对子树,直到发现不对称或全部验证通过,返回最终结果。
代码 1
class Solution {
// 辅助方法:比较两个节点是否对称
public boolean compareNode(TreeNode left, TreeNode right) {
// 情况1:左节点为空,右节点不为空,不对称,返回 false
if (left == null && right != null) return false;
// 情况2:左节点不为空,右节点为空,不对称,返回 false
else if (left != null && right == null) return false;
// 情况3:左右节点都为空,对称,返回 true
else if (left == null && right == null) return true;
// 情况4:左右节点都不为空,但值不相等,不对称,返回 false
else if (left.val != right.val) return false;
// 情况5:左右节点值相等,递归检查:
// - 左子树的左子节点与右子树的右子节点是否对称
// - 左子树的右子节点与右子树的左子节点是否对称
else return compareNode(left.left, right.right) && compareNode(left.right, right.left);
}
// 主方法:检查整棵树是否轴对称
public boolean isSymmetric(TreeNode root) {
// 从根节点的左右子树开始比较
return compareNode(root.left, root.right);
}
}
解题思路2
检查二叉树是否轴对称的迭代方法通过队列模拟层级对称比较实现。从根节点的左右子树开始,将每一对需要比较的节点成对加入队列,每次从队列中取出两个节点,检查它们是否对称(都为空、或都不为空且值相等),然后按镜像顺序(左左-右右,左右-右左)将子节点入队,继续比较,直到队列为空或发现不对称为止。
代码2
class Solution {
public boolean isSymmetric(TreeNode root) {
// 创建一个队列,用于存储待比较的节点对
LinkedList<TreeNode> queue = new LinkedList<>();
// 将根节点的左子树和右子树加入队列,作为第一对比较对象
queue.offer(root.left);
queue.offer(root.right);
// 当队列不为空时,持续比较节点对
while (!queue.isEmpty()) {
// 创建临时节点,用于接收队列弹出的左右节点
TreeNode leftNode = new TreeNode();
TreeNode rightNode = new TreeNode();
// 从队列中取出当前比较的左右节点
leftNode = queue.poll();
rightNode = queue.poll();
// 情况1:左右节点都为空,对称,继续下一轮循环
if (leftNode == null && rightNode == null) {
continue;
}
// 情况2:一个为空一个不为空,或值不相等,不对称,返回 false
else if ((leftNode == null && rightNode != null) ||
(leftNode != null && rightNode == null) ||
(leftNode.val != rightNode.val)) {
return false;
}
// 情况3:左右节点都不为空且值相等,继续检查子节点
else {
// 将左子树的左子节点和右子树的右子节点加入队列
queue.offer(leftNode.left);
queue.offer(rightNode.right);
// 将左子树的右子节点和右子树的左子节点加入队列
queue.offer(leftNode.right);
queue.offer(rightNode.left);
}
}
// 队列为空且未发现不对称,返回 true
return true;
}
}
100.相同的树
给你两棵二叉树的根节点
p
和q
,编写一个函数来检验这两棵树是否相同。如果两个树在结构上相同,并且节点具有相同的值,则认为它们是相同的。
示例 1:
输入:p = [1,2,3], q = [1,2,3] 输出:true示例 2:
输入:p = [1,2], q = [1,null,2] 输出:false示例 3:
输入:p = [1,2,1], q = [1,1,2] 输出:false提示:
- 两棵树上的节点数目都在范围
[0, 100]
内-104 <= Node.val <= 104
解题思路1
检查两棵二叉树是否相同的方法通过递归比较节点值和结构实现。从根节点开始,判断两个节点是否都为空(相同)、一个为空一个不为空或值不同(不相同),若都不为空且值相等,则递归比较它们的左子树和右子树,只有当所有对应节点的值和结构都一致时返回 true,否则返回 false。
代码 1
class Solution {
// 辅助方法:递归比较两个节点及其子树是否相同
public boolean fun1(TreeNode leftNode, TreeNode rightNode) {
// 情况1:两个节点都为空,结构和值相同,返回 true
if (leftNode == null && rightNode == null) {
return true;
}
// 情况2:一个节点为空,另一个不为空,或值不相等,不相同,返回 false
else if ((leftNode == null && rightNode != null) ||
(leftNode != null && rightNode == null) ||
(leftNode.val != rightNode.val)) {
return false;
}
// 情况3:两个节点都不为空且值相等,递归比较左右子树
else {
// 检查左子树是否相同 && 检查右子树是否相同
return fun1(leftNode.left, rightNode.left) && fun1(leftNode.right, rightNode.right);
}
}
// 主方法:检查两棵树是否完全相同
public boolean isSameTree(TreeNode p, TreeNode q) {
// 从根节点开始比较两棵树
return fun1(p, q);
}
}
解题思路2
检查两棵二叉树是否相同的迭代方法通过队列逐层比较节点对实现。从根节点开始,将两棵树的对应节点成对加入队列,每次从队列中取出两个节点,检查它们是否都为空(相同)、一个为空一个不为空或值不同(不相同),若都不为空且值相等,则将它们的左右子节点按顺序入队,继续比较,直到队列为空或发现不同为止。
代码2
class Solution {
public boolean isSameTree(TreeNode p, TreeNode q) {
// 创建一个队列,用于存储待比较的节点对
LinkedList<TreeNode> queue = new LinkedList<>();
// 将两棵树的根节点加入队列,作为第一对比较对象
queue.offer(p);
queue.offer(q);
// 当队列不为空时,持续比较节点对
while (!queue.isEmpty()) {
// 创建临时节点,用于接收队列弹出的左右节点
TreeNode leftNode = new TreeNode();
TreeNode rightNode = new TreeNode();
// 从队列中取出当前比较的节点(分别来自 p 和 q)
leftNode = queue.poll();
rightNode = queue.poll();
// 情况1:两个节点都为空,相同,继续下一轮循环
if (leftNode == null && rightNode == null) {
continue;
}
// 情况2:一个为空一个不为空,或值不相等,不相同,返回 false
else if ((leftNode == null && rightNode != null) ||
(leftNode != null && rightNode == null) ||
(leftNode.val != rightNode.val)) {
return false;
}
// 情况3:两个节点都不为空且值相等,继续检查子节点
else {
// 将左子树节点对加入队列
queue.offer(leftNode.left);
queue.offer(rightNode.left);
// 将右子树节点对加入队列
queue.offer(leftNode.right);
queue.offer(rightNode.right);
}
}
// 队列为空且未发现不同,返回 true
return true;
}
}
572.另一棵树的子树
给你两棵二叉树
root
和subRoot
。检验root
中是否包含和subRoot
具有相同结构和节点值的子树。如果存在,返回true
;否则,返回false
。二叉树
tree
的一棵子树包括tree
的某个节点和这个节点的所有后代节点。tree
也可以看做它自身的一棵子树。示例 1:
输入:root = [3,4,5,1,2], subRoot = [4,1,2] 输出:true示例 2:
输入:root = [3,4,5,1,2,null,null,null,null,0], subRoot = [4,1,2] 输出:false提示:
root
树上的节点数量范围是[1, 2000]
subRoot
树上的节点数量范围是[1, 1000]
-104 <= root.val <= 104
-104 <= subRoot.val <= 104
解题思路
判断一棵二叉树是否包含另一棵子树的核心在于结合层序遍历和子树匹配:通过队列对 root 进行层序遍历,找到所有值与 subRoot 根节点相等的节点,然后使用递归方法 fun1 检查以这些节点为根的子树是否与 subRoot 完全相同(结构和值一致),只要找到一个匹配的子树即返回 true,否则遍历结束后返回 false。
代码
class Solution {
// 辅助方法:递归比较两个节点及其子树是否完全相同
public boolean fun1(TreeNode leftNode, TreeNode rightNode) {
// 情况1:两个节点都为空,相同,返回 true
if (leftNode == null && rightNode == null) {
return true;
}
// 情况2:一个节点为空一个不为空,或值不相等,不相同,返回 false
else if ((leftNode == null && rightNode != null) ||
(leftNode != null && rightNode == null) ||
(leftNode.val != rightNode.val)) {
return false;
}
// 情况3:两个节点都不为空且值相等,递归比较左右子树
else {
return fun1(leftNode.left, rightNode.left) && fun1(leftNode.right, rightNode.right);
}
}
// 主方法:检查 root 中是否包含 subRoot 作为子树
public boolean isSubtree(TreeNode root, TreeNode subRoot) {
// 使用队列进行层序遍历,寻找可能的子树根节点
Queue<TreeNode> queue = new ArrayDeque<>();
// 将 root 加入队列,从根节点开始遍历
queue.offer(root);
// 当队列不为空时,持续检查每一层节点
while (!queue.isEmpty()) {
// 获取当前层的节点数
int size = queue.size();
// 遍历当前层的所有节点
while (size-- > 0) {
// 创建临时节点,接收队列弹出的节点
TreeNode node = new TreeNode();
node = queue.poll();
// 如果当前节点值与 subRoot 的根节点值相等,检查是否为相同子树
if (node.val == subRoot.val) {
if (fun1(node, subRoot)) {
// 如果子树完全相同,返回 true
return true;
}
}
// 将左子节点加入队列(如果存在)
if (node.left != null) queue.offer(node.left);
// 将右子节点加入队列(如果存在)
if (node.right != null) queue.offer(node.right);
}
}
// 未找到匹配的子树,返回 false
return false;
}
}
559.N叉树的最大深度
给定一个 N 叉树,找到其最大深度。
最大深度是指从根节点到最远叶子节点的最长路径上的节点总数。
N 叉树输入按层序遍历序列化表示,每组子节点由空值分隔(请参见示例)。
示例 1:
输入:root = [1,null,3,2,4,null,5,6] 输出:3示例 2:
输入:root = [1,null,2,3,4,5,null,null,6,7,null,8,null,9,10,null,null,11,null,12,null,13,null,null,14] 输出:5提示:
- 树的深度不会超过
1000
。- 树的节点数目位于
[0, 104]
之间。
解题思路
计算 N 叉树最大深度的核心在于通过层序遍历逐层计数:从根节点开始,使用队列存储每层节点,依次处理每一层,将当前节点的子节点加入队列,每完成一层深度加 1,直到队列为空时,返回总层数即为从根到最远叶子节点的最大深度。
代码
class Solution {
public int maxDepth(Node root) {
// 创建一个双端队列,用于层序遍历
Deque<Node> queue = new ArrayDeque<>();
// 初始化深度计数器
int length = 0;
// 如果根节点为空,返回深度 0
if (root == null) {
return 0;
}
// 将根节点加入队列,作为第一层
queue.offer(root);
// 当队列不为空时,持续处理每一层节点
while (!queue.isEmpty()) {
// 获取当前层的节点数
int size = queue.size();
// 遍历当前层的所有节点
while (size-- > 0) {
// 创建临时节点,接收队列弹出的节点
Node node = new Node();
node = queue.poll();
// 将当前节点的所有子节点加入队列
for (Node node1 : node.children) {
queue.offer(node1);
}
}
// 当前层处理完毕,深度加 1
length++;
}
// 返回最大深度
return length;
}
}
222.完全二叉树的节点个数
给你一棵 完全二叉树 的根节点
root
,求出该树的节点个数。完全二叉树 的定义如下:在完全二叉树中,除了最底层节点可能没填满外,其余每层节点数都达到最大值,并且最下面一层的节点都集中在该层最左边的若干位置。若最底层为第
h
层(从第 0 层开始),则该层包含1~ 2h
个节点。示例 1:
输入:root = [1,2,3,4,5,6] 输出:6示例 2:
输入:root = [] 输出:0示例 3:
输入:root = [1] 输出:1提示:
- 树中节点的数目范围是
[0, 5 * 104]
0 <= Node.val <= 5 * 104
- 题目数据保证输入的树是 完全二叉树
进阶:遍历树来统计节点是一种时间复杂度为
O(n)
的简单解决方案。你可以设计一个更快的算法吗?
解题思路
计算完全二叉树节点数的核心在于利用其结构特性优化递归:对于每个子树,计算从根到最左叶子的路径长度(leftLen)和从根到最右叶子的路径长度(rightLen),若两者相等,则该子树是满二叉树,节点数可直接用公式 2^(h+1) - 1 计算(h 是高度);若不相等,则递归计算左右子树的节点数并加上根节点,通过这种方式减少不必要的节点遍历。
代码
class Solution {
public int countNodes(TreeNode root) {
// 如果根节点为空,返回节点数 0
if (root == null) return 0;
// 创建临时节点,分别指向左子节点和右子节点
TreeNode leftNode = new TreeNode();
TreeNode rightNode = new TreeNode();
leftNode = root.left; // 指向左子树
rightNode = root.right; // 指向右子树
// 计算左子树的最左路径长度
int leftLen = 0;
while (leftNode != null) {
leftLen++;
leftNode = leftNode.left; // 一直向左走
}
// 计算右子树的最右路径长度
int rightLen = 0;
while (rightNode != null) {
rightLen++;
rightNode = rightNode.right; // 一直向右走
}
// 如果左路径长度等于右路径长度,说明是满二叉树
if (leftLen == rightLen) {
// 满二叉树的节点数公式:2^(高度+1) - 1,这里 leftLen 是高度
return (2 << leftLen) - 1;
}
// 如果不是满二叉树,递归计算左子树和右子树的节点数,再加上根节点
return countNodes(root.left) + countNodes(root.right) + 1;
}
}
110.平衡二叉树
给定一个二叉树,判断它是否是 平衡二叉树
示例 1:
输入:root = [3,9,20,null,null,15,7] 输出:true示例 2:
输入:root = [1,2,2,3,3,null,null,4,4] 输出:false示例 3:
输入:root = [] 输出:true提示:
- 树中的节点数在范围
[0, 5000]
内-104 <= Node.val <= 104
解题思路
判断二叉树是否平衡的核心在于自底向上递归检查每个节点的左右子树高度差:通过 getHeight 方法计算以每个节点为根的子树高度,若某节点左右子树高度差超过 1,则标记为不平衡(返回 -1),递归过程中一旦发现不平衡就提前终止,最终根据根节点的返回值判断整棵树是否平衡
代码
class Solution {
// 辅助方法:计算树的高度,同时检查是否平衡
public int getHeight(TreeNode root) {
// 如果节点为空,返回高度 0
if (root == null) return 0;
// 递归计算左子树高度
int leftHeight = getHeight(root.left);
// 如果左子树已不平衡(返回 -1),直接返回 -1
if (leftHeight == -1) return -1;
// 递归计算右子树高度
int rightHeight = getHeight(root.right);
// 如果右子树已不平衡(返回 -1),直接返回 -1
if (rightHeight == -1) return -1;
// 检查当前节点的左右子树高度差是否超过 1
if (Math.abs(leftHeight - rightHeight) > 1) {
// 高度差大于 1,不平衡,返回 -1
return -1;
}
// 当前节点平衡,返回以该节点为根的子树高度(最大子树高度 + 1)
return Math.max(leftHeight, rightHeight) + 1;
}
// 主方法:判断整棵树是否平衡
public boolean isBalanced(TreeNode root) {
// 如果 getHeight 返回 -1,说明不平衡,返回 false;否则返回 true
return getHeight(root) > -1;
}
}
257.二叉树的所有路径
给你一个二叉树的根节点
root
,按 任意顺序 ,返回所有从根节点到叶子节点的路径。叶子节点 是指没有子节点的节点。
示例 1:
输入:root = [1,2,3,null,5] 输出:["1->2->5","1->3"]示例 2:
输入:root = [1] 输出:["1"]提示:
- 树中节点的数目在范围
[1, 100]
内-100 <= Node.val <= 100
解题思路
返回二叉树所有根到叶子路径的核心在于递归深度优先搜索结合回溯:从根节点开始,将当前节点值加入路径列表,递归遍历左右子树,当到达叶子节点时将路径转换为字符串保存到结果列表,之后通过回溯移除当前节点值,以便正确构建其他路径,最终返回所有完整路径。
代码
class Solution {
// 定义全局列表,用于存储所有从根到叶子的路径
List<String> ans = new ArrayList<>();
// 辅助方法:将路径值列表转换为字符串表示
public String output(ArrayList<Integer> val) {
// 使用 StringBuilder 高效拼接字符串
StringBuilder sb = new StringBuilder();
int size = val.size();
// 遍历路径中的每个节点值
for (int i = 0; i < size; i++) {
if (i < size - 1) {
// 非最后一个节点,添加值和箭头 "->"
sb.append(val.get(i)).append("->");
} else {
// 最后一个节点,只添加值
sb.append(val.get(i));
}
}
// 返回拼接好的路径字符串
return sb.toString();
}
// 递归方法:构建从根到叶子的路径
public void fun1(TreeNode root, ArrayList<Integer> val) {
// 将当前节点值加入路径
val.add(root.val);
// 如果是叶子节点(无左右子节点),保存当前路径
if (root.left == null && root.right == null) {
ans.add(output(val));
} else {
// 如果有左子节点,递归处理左子树
if (root.left != null) fun1(root.left, val);
// 如果有右子节点,递归处理右子树
if (root.right != null) fun1(root.right, val);
}
// 回溯:移除当前节点值,以便处理其他路径
val.remove(val.size() - 1);
}
// 主方法:返回所有根到叶子路径
public List<String> binaryTreePaths(TreeNode root) {
// 初始化路径值列表
ArrayList<Integer> val = new ArrayList<>();
// 调用递归方法开始处理
fun1(root, val);
// 返回结果列表
return ans;
}
}