二叉树相关
104. 二叉树的最大深度
问题
给定一个二叉树 root ,返回其最大深度。
二叉树的 最大深度 是指从根节点到最远叶子节点的最长路径上的节点数。
示例 1:

输入:root = [3,9,20,null,null,15,7]
输出:3示例 2:
输入:root = [1,null,2]
输出:2提示
不断递归到最后一层的节点,当节点为null时返回0,而每一层节点都比较左右子树的最大深度,返回当前节点加1深度。
示例
/**
* 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 int maxDepth(TreeNode root) {
// 当前节点为null说明到底了
if(root == null) return 0;
// 比较左右子树,返回最大深度+1
return 1 + Math.max(maxDepth(root.left), maxDepth(root.right));
}
}543. 二叉树的直径
问题
给你一棵二叉树的根节点,返回该树的 直径 。
二叉树的 直径 是指树中任意两个节点之间最长路径的 长度 。这条路径可能经过也可能不经过根节点 root 。
两节点之间路径的 长度 由它们之间边数表示。
示例 1:

输入:root = [1,2,3,4,5]
输出:3
解释:3 ,取路径 [4,2,1,3] 或 [5,2,1,3] 的长度。示例 2:
输入:root = [1,2]
输出:1提示
遍历所有节点,以每个节点为中心点,左右子树的最大深度的和就是当前节点的最大直径,用一个全局变量不断得到最大的值最后返回即可。
示例
/**
* 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 {
// 全局变量保存最大直径
int maxDiameter = 0;
public int diameterOfBinaryTree(TreeNode root) {
if(root == null) return 0;
maxDepth(root);
return maxDiameter;
}
private int maxDepth(TreeNode root) {
if(root == null) return 0;
// 分别获得左右子树的最大深度
int left = maxDepth(root.left);
int right = maxDepth(root.right);
// 比较当前节点的直径与全局最大直径作比较
maxDiameter = Math.max(maxDiameter, left+right);
// 返回当前节点的最大深度
return 1 + Math.max(left, right);
}
}102. 二叉树的层序遍历
问题
给你二叉树的根节点 root ,返回其节点值的 层序遍历 。 (即逐层地,从左到右访问所有节点)。
示例 1:

输入:root = [3,9,20,null,null,15,7]
输出:[[3],[9,20],[15,7]]示例 2:
输入:root = [1]
输出:[[1]]示例 3:
输入:root = []
输出:[]提示
利用队列先进先出的特性来做,队列中放入头节点,然后遍历队列,每次从队列中取出一个节点就紧接着将当前节点的左右子节点放入队列,由于这题需要我们把每层的值封装成一个list,所以我们每次进入循环就先看队列的size也就是当前层的节点个数,然后取出size个节点再进入下次循环。
示例
/**
* 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 List<List<Integer>> levelOrder(TreeNode root) {
if(root==null) return new ArrayList<>();
// 存储结果列表
List<List<Integer>> lists = new ArrayList<>();
// 节点队列
Queue<TreeNode> queue = new LinkedList<>();
// 存入根节点
queue.add(root);
while(!queue.isEmpty()){
// 使用size可以保证只便利当前层的节点
int size = queue.size();
List<Integer> list = new ArrayList<>();
while(size-->0){
TreeNode temp = queue.poll();
list.add(temp.val);
// 每次都将当前节点的左右子节点存入队列
if(temp.left!=null) queue.add(temp.left);
if(temp.right!=null) queue.add(temp.right);
}
lists.add(list);
}
return lists;
}
}114. 二叉树展开为链表
问题
给你二叉树的根结点 root ,请你将它展开为一个单链表:
- 展开后的单链表应该同样使用
TreeNode,其中right子指针指向链表中下一个结点,而左子指针始终为null。 - 展开后的单链表应该与二叉树 先序遍历 顺序相同。
示例 1:

输入:root = [1,2,5,3,4,null,6]
输出:[1,null,2,null,3,null,4,null,5,null,6]示例 2:
输入:root = []
输出:[]示例 3:
输入:root = [0]
输出:[0]提示
使用后序遍历模型,先递归调用函数展开左右子树,然后对展开后的左右子树与根节点进行连接,先连接左子树,再将根节点的左节点置空,然后用临时节点遍历得到当前“链表”的尾节点,尾节点与右子树连接。
示例
/**
* 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 void flatten(TreeNode root) {
if(root == null) return;
// 先遍历左右子节点
flatten(root.left);
flatten(root.right);
// 取得左右子节点
TreeNode left = root.left;
TreeNode right = root.right;
// 根节点先链接左节点
root.right = left;
root.left = null;
// 遍历到最右侧节点
TreeNode temp = root;
while(temp.right != null) {
temp = temp.right;
}
// 链接原本的右节点
temp.right = right;
}
}116. 填充每个节点的下一个右侧节点指针
问题
给定一个 完美二叉树 ,其所有叶子节点都在同一层,每个父节点都有两个子节点。二叉树定义如下:
struct Node {
int val;
Node *left;
Node *right;
Node *next;
}填充它的每个 next 指针,让这个指针指向其下一个右侧节点。如果找不到下一个右侧节点,则将 next 指针设置为 NULL。
初始状态下,所有 next 指针都被设置为 NULL。
示例 1:

输入:root = [1,2,3,4,5,6,7]
输出:[1,#,2,3,#,4,5,6,7,#]
解释:给定二叉树如图 A 所示,你的函数应该填充它的每个 next 指针,以指向其下一个右侧节点,如图 B 所示。序列化的输出按层序遍历排列,同一层节点由 next 指针连接,'#' 标志着每一层的结束。示例 2:
输入:root = []
输出:[]提示
前序遍历模型,每层节点去处理下层节点的连接工作,先连接左右子节点,如果当前节点有next节点的话,就让右子节点连接next节点的左子节点,然后递归调用函数,处理左右子节点。
示例
/*
// Definition for a Node.
class Node {
public int val;
public Node left;
public Node right;
public Node next;
public Node() {}
public Node(int _val) {
val = _val;
}
public Node(int _val, Node _left, Node _right, Node _next) {
val = _val;
left = _left;
right = _right;
next = _next;
}
};
*/
class Solution {
/**
依旧是递归思想, 前序遍历模型,
思路就是每层去处理下一层的节点, 当前层的节点已经在上层连接好了
*/
public Node connect(Node root) {
if(root == null) return null;
// 当前节点还有下层时才进行连接操作
if(root.left != null) {
// 先连接左右子节点
root.left.next = root.right;
// 如果当前节点有next节点
if(root.next != null) {
// 就将此节点的右子节点与next节点的左子节点进行连接
root.right.next = root.next.left;
}
}
// 前序遍历, 处理左右子节点
connect(root.left);
connect(root.right);
return root;
}
}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 = []
输出:[]提示
前序遍历框架,先交换左右节点,然后递归调用函数处理左右子树。
示例
/**
* 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 TreeNode invertTree(TreeNode root) {
if(root == null) return null;
// 调换左右节点
TreeNode temp = root.left;
root.left = root.right;
root.right = temp;
// 递归处理
invertTree(root.left);
invertTree(root.right);
return root;
}
}652. 寻找重复的子树
问题
给你一棵二叉树的根节点 root ,返回所有 重复的子树 。
对于同一类的重复子树,你只需要返回其中任意 一棵 的根结点即可。
如果两棵树具有 相同的结构 和 相同的结点值 ,则认为二者是 重复 的。
示例 1:

输入:root = [1,2,3,4,null,2,4,null,null,4]
输出:[[2,4],[4]]示例 2:

输入:root = [2,1,1]
输出:[[1]]示例 3:

输入:root = [2,2,2,3,null,3,null]
输出:[[2,3],[3]]提示
比较一个二叉树是很困难的,但是比较一个字符串很简单,所以我们将二叉树序列化,使用后序遍历框架,为什么是后序遍历呢,要想啊,我们肯定是要遍历所有节点,然后以各个节点为根节点查看当前子树构造并判断是否重复,要想知道以当前节点为根节点的子树肯定是要先知道自己的左右子树长什么样,得到序列串后在全局哈希表中进行判断,把满足条件的节点放入全局结果集中。
示例
/**
* 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 {
/**
比较树我们不好比较, 但是将树序列化成字符串就好比较了
这里我们使用后序遍历框架, 为啥要用后序呢?
因为我们比较子树是需要遍历所有节点, 并且以当前节点为根节点进行比较
后序遍历我们就可以先知道左右子树情况
*/
// 全局结果集
private List<TreeNode> res = new ArrayList<>();
// 哈希表存储序列串和出现次数
private Map<String, Integer> map = new HashMap<>();
public List<TreeNode> findDuplicateSubtrees(TreeNode root) {
// 调用辅助函数
traverse(root);
// 返回全局List
return res;
}
// 辅助函数, 帮助我们遍历二叉树
private String traverse(TreeNode root) {
// 用#表示空节点
if(root == null) return "#";
String left = traverse(root.left);
String right = traverse(root.right);
// 将左右子树与根节点组合成序列化字符串
String subTree = left + "," + right + "," + root.val;
int count = map.getOrDefault(subTree, 0);
if (count == 1) {
// 由于多个重复子树只返回一次根节点, 所以只有当次数为1时, 放入结果集
res.add(root);
}
// 将此序列串的值+1
map.put(subTree, count + 1);
return subTree;
}
}