day10
515. 在每个树行中找最大值
题目
给定一棵二叉树的根节点 root
,请找出该二叉树中每一层的最大值。
示例1:
输入: root = [1,3,2,5,3,null,9]
输出: [1,3,9]
示例2:
输入: root = [1,2,3]
输出: [1,3]
提示:
二叉树的节点个数的范围是 [0,10^4]
-2^31 <= Node.val <= 2^31 - 1
思路
层序遍历,取每一层的最大值
代码实现
/**
* https://leetcode-cn.com/problems/find-largest-value-in-each-tree-row/
*
* @author xiexu
* @create 2022-03-09 11:15 AM
*/
public class _515_在每个树行中找最大值 {
public List<Integer> largestValues(TreeNode root) {
List<Integer> res = new ArrayList<>();
Queue<TreeNode> queue = new LinkedList<>();
if (root != null) {
queue.offer(root);
}
while (!queue.isEmpty()) {
int n = queue.size();
ArrayList<Integer> list = new ArrayList<>();
for (int i = 0; i < n; i++) {
TreeNode node = queue.poll();
list.add(node.val);
if (node.left != null) {
queue.offer(node.left);
}
if (node.right != null) {
queue.offer(node.right);
}
}
res.add(Collections.max(list));
}
return res;
}
}
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 = []
输出:[]
提示:
树中节点的数量在 [0, 2^12 - 1] 范围内
-1000 <= node.val <= 1000
思路
本题依然是层序遍历,只不过在单层遍历的时候记录一下本层的头部节点,然后在遍历的时候让前一个节点指向本节点就可以了
代码实现
/**
* https://leetcode-cn.com/problems/populating-next-right-pointers-in-each-node/
*
* @author xiexu
* @create 2022-03-09 11:25 AM
*/
public class _116_填充每个节点的下一个右侧节点指针 {
public Node connect(Node root) {
Queue<Node> queue = new LinkedList<>();
if (root != null) {
queue.add(root);
}
while (!queue.isEmpty()) {
int n = queue.size();
Node node = null;
Node preNode = null; // 头结点
for (int i = 0; i < n; i++) {
if (i == 0) {
preNode = queue.poll(); // 取出本层头一个节点
node = preNode;
} else {
node = queue.poll();
preNode.next = node;
preNode = preNode.next;
}
if (node.left != null) {
queue.offer(node.left);
}
if (node.right != null) {
queue.offer(node.right);
}
}
preNode.next = null; // 本层最后一个节点 next 指向 null
}
return root;
}
}
117. 填充每个节点的下一个右侧节点指针 II
题目
给定一个二叉树
struct Node {
int val;
Node *left;
Node *right;
Node *next;
}
填充它的每个 next 指针,让这个指针指向其下一个右侧节点。如果找不到下一个右侧节点,则将 next 指针设置为 NULL。
初始状态下,所有 next 指针都被设置为 NULL。
示例:
输入:root = [1,2,3,4,5,null,7]
输出:[1,#,2,3,#,4,5,7,#]
解释:给定二叉树如图 A 所示,你的函数应该填充它的每个 next 指针,以指向其下一个右侧节点,如图 B 所示。序列化输出按层序遍历顺序(由 next 指针连接),'#' 表示每层的末尾。
代码实现
/**
* https://leetcode-cn.com/problems/populating-next-right-pointers-in-each-node-ii/
*
* @author xiexu
* @create 2022-03-09 11:40 AM
*/
public class _117_填充每个节点的下一个右侧节点指针_II {
public Node connect(Node root) {
Queue<Node> queue = new LinkedList<>();
if (root != null) {
queue.offer(root);
}
while (!queue.isEmpty()) {
int n = queue.size();
Node node = null;
Node preNode = null; // 头结点
for (int i = 0; i < n; i++) {
if (i == 0) {
preNode = queue.poll(); // 取出本层头一个节点
node = preNode;
} else {
node = queue.poll();
preNode.next = node;
preNode = preNode.next;
}
if (node.left != null) {
queue.offer(node.left);
}
if (node.right != null) {
queue.offer(node.right);
}
}
preNode.next = null; // 本层最后一个节点 next 指向 null
}
return root;
}
}
104. 二叉树的最大深度
题目
给定一个二叉树,找出其最大深度。
二叉树的深度为根节点到最远叶子节点的最长路径上的节点数。
说明: 叶子节点是指没有子节点的节点。
示例:
给定二叉树 [3,9,20,null,null,15,7]
,
3
/ \
9 20
/ \
15 7
返回它的最大深度 3 。
思路
使用迭代法的话,使用层序遍历是最为合适的,因为最大的深度就是二叉树的层数,和层序遍历的方式极其吻合。
在二叉树中,一层一层的来遍历二叉树,记录一下遍历的层数就是二叉树的深度,如图所示:
代码实现
/**
* https://leetcode-cn.com/problems/maximum-depth-of-binary-tree/
*
* @author xiexu
* @create 2022-03-09 12:34 PM
*/
public class _104_二叉树的最大深度 {
public int maxDepth(TreeNode root) {
if (root == null) {
return 0;
}
Queue<TreeNode> queue = new LinkedList<>();
// 深度计数
int depth = 0;
queue.offer(root);
while (!queue.isEmpty()) {
int n = queue.size();
while (n > 0) {
TreeNode node = queue.poll();
if (node.left != null) {
queue.offer(node.left);
}
if (node.right != null) {
queue.offer(node.right);
}
n--;
}
depth++;
}
return depth;
}
}
111. 二叉树的最小深度
题目
给定一个二叉树,找出其最小深度。
最小深度是从根节点到最近叶子节点的最短路径上的节点数量。
**说明:**叶子节点是指没有子节点的节点。
示例 1:
输入:root = [3,9,20,null,null,15,7]
输出:2
示例 2:
输入:root = [2,null,3,null,4,null,5,null,6]
输出:5
提示:
树中节点数的范围在 [0, 10^5] 内
-1000 <= Node.val <= 1000
思路
需要注意的是,只有当左右孩子都为空的时候,才说明遍历的最低点了。如果其中一个孩子为空则不是最低点
代码实现
/**
* https://leetcode-cn.com/problems/minimum-depth-of-binary-tree/
*
* @author xiexu
* @create 2022-03-09 12:41 PM
*/
public class _111_二叉树的最小深度 {
public int minDepth(TreeNode root) {
if (root == null) {
return 0;
}
Queue<TreeNode> queue = new LinkedList<>();
// 最小深度
int depth = 0;
queue.offer(root);
while (!queue.isEmpty()) {
int n = queue.size();
depth++;
for (int i = 0; i < n; i++) {
TreeNode node = queue.poll();
// 如果当前节点的左右孩子都为空,直接返回最小深度
if (node.left == null && node.right == null) {
return depth;
}
if (node.left != null) {
queue.offer(node.left);
}
if (node.right != null) {
queue.offer(node.right);
}
}
}
return depth;
}
}
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
时返回 - 交换当前节点的左右节点,再递归的交换当前节点的左节点,递归的交换当前节点的右节点
代码实现
/**
* https://leetcode-cn.com/problems/invert-binary-tree/
*
* @author xiexu
* @create 2022-03-09 1:19 PM
*/
public class _226_翻转二叉树 {
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;
}
}
迭代解法
思路
递归实现也就是深度优先遍历的方式,那么对应的就是广度优先遍历。
广度优先遍历需要额外的数据结构 — 队列,来存放临时遍历到的元素。
深度优先遍历的特点是一竿子插到底,不行了再退回来继续;而广度优先遍历的特点是层层扫荡。
所以,我们需要先将根节点放入到队列中,然后不断的迭代队列中的元素。
对当前元素调换其左右子树的位置,然后:
- 判断其左子树是否为空,不为空就放入队列中
- 判断其右子树是否为空,不为空就放入队列中
代码实现
/**
* https://leetcode-cn.com/problems/invert-binary-tree/
*
* @author xiexu
* @create 2022-03-09 3:24 PM
*/
public class _226_翻转二叉树_迭代 {
public TreeNode invertTree(TreeNode root) {
Queue<TreeNode> queue = new LinkedList<>();
if (root != null) {
queue.offer(root);
}
while (!queue.isEmpty()) {
int n = queue.size();
for (int i = 0; i < n; i++) {
// 每次都从队列中拿一个节点,并交换这个节点的左右子树
TreeNode node = queue.poll();
TreeNode temp = node.left;
node.left = node.right;
node.right = temp;
// 如果当前节点的左子树不为空,则放入队列等待后续处理
if (node.left != null) {
queue.offer(node.left);
}
// 如果当前节点的右子树不为空,则放入队列等待后续处理
if (node.right != null) {
queue.offer(node.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
递归解法
思路
我们将根节点的左子树记做 left
,右子树记做 right
。比较 left
是否等于 right
,不等的话直接返回就可以了。
如果相等,比较 left
的左节点和 right
的右节点,再比较 left
的右节点和 right
的左节点
比如看下面这两个子树(他们分别是根节点的左子树和右子树),能观察到这么一个规律:
2 2
/ \ / \
3 4 4 3
/ \ / \ / \ / \
8 7 6 5 5 6 7 8
- 左子树 2 的左孩子 == 右子树 2 的右孩子
- 左子树 2 的右孩子 == 右子树 2 的左孩子
终止条件:
left
和right
不等,或者left
和right
都为空- 递归的比较
left.left
和right.right
,递归比较left.right
和right.left
代码实现
/**
* https://leetcode-cn.com/problems/symmetric-tree/
*
* @author xiexu
* @create 2022-03-09 3:39 PM
*/
public class _101_对称二叉树 {
public boolean isSymmetric(TreeNode root) {
if (root == null) {
return true;
}
// 调用递归函数,比较左节点、右节点
return dfs(root.left, root.right);
}
public boolean dfs(TreeNode left, TreeNode right) {
// 递归的终止条件是两个节点都为空
// 或者两个节点中有一个为空
// 或者两个节点的值不相等
if (left == null && right == null) {
return true;
}
if (left == null || right == null) {
return false;
}
if (left.val != right.val) {
return false;
}
// 再递归的比较 左节点的左孩子 和 右节点的右孩子
// 以及比较 左节点的右孩子 和 右节点的左孩子
return dfs(left.left, right.right) && dfs(left.right, right.left);
}
}
迭代解法
思路
- 首先从队列中拿出两个节点(
left
和right
)比较 - 将
left
的left
节点和right
的right
节点放入队列 - 将
left
的right
节点和right
的left
节点放入队列
代码实现
/**
* https://leetcode-cn.com/problems/symmetric-tree/
*
* @author xiexu
* @create 2022-03-09 4:12 PM
*/
public class _101_对称二叉树_迭代 {
public boolean isSymmetric(TreeNode root) {
if (root == null || (root.left == null && root.right == null)) {
return true;
}
// 用队列保存节点
Queue<TreeNode> queue = new LinkedList<>();
// 将根节点的左右孩子放到队列中
queue.offer(root.left);
queue.offer(root.right);
while (!queue.isEmpty()) {
// 从队列中取出两个节点,再比较这两个节点
TreeNode left = queue.poll();
TreeNode right = queue.poll();
// 如果两个节点都为空就继续循环,两者有一个为空就返回false
if (left == null && right == null) {
continue;
}
if (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;
}
}
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] 内
-10^4 <= Node.val <= 10^4
思路
终止条件与返回值:
- 当两棵树的当前节点都为 null 时返回
true
- 当其中一个为 null 另一个不为 null 时返回
false
- 当两个都不为空但是值不相等时,返回
false
代码实现
/**
* https://leetcode-cn.com/problems/same-tree/
*
* @author xiexu
* @create 2022-03-09 4:27 PM
*/
public class _100_相同的树 {
public boolean isSameTree(TreeNode p, TreeNode q) {
if (p == null && q == null) {
return true;
}
if (p == null || q == null) {
return false;
}
if (p.val != q.val) {
return false;
}
return isSameTree(p.left, q.left) && isSameTree(p.right, q.right);
}
}
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]
-10^4 <= root.val <= 10^4
-10^4 <= subRoot.val <= 10^4
代码实现
/**
* https://leetcode-cn.com/problems/subtree-of-another-tree/
*
* @author xiexu
* @create 2022-03-09 4:37 PM
*/
public class _572_另一棵树的子树 {
public boolean isSubtree(TreeNode root, TreeNode subRoot) {
if (root == null) {
return subRoot == null;
}
// 判断以 root 为根的二叉树是否和 subRoot 相同
if (isSameTree(root, subRoot)) {
return true;
}
// 去左右子树中判断是否有和 subRoot 相同的子树
return isSubtree(root.left, subRoot) || isSubtree(root.right, subRoot);
}
public boolean isSameTree(TreeNode p, TreeNode q) {
if (p == null && q == null) {
return true;
}
if (p == null || q == null) {
return false;
}
if (p.val != q.val) {
return false;
}
return isSameTree(p.left, q.left) && isSameTree(p.right, q.right);
}
}