【LeetCode】剑指 Offer Ⅱ 第8章:树(12道题) -- Java Version
题库链接:https://leetcode.cn/problem-list/e8X3pBZi/

| 类型 | 题目 | 解决方案 |
|---|---|---|
| 二叉树的深搜 | 剑指 Offer II 047. 二叉树剪枝 | 递归(深搜):二叉树的后序遍历 (⭐) |
| 剑指 Offer II 048. 序列化和反序列化二叉树 | 递归(深搜):二叉树的前序遍历(⭐) | |
| 剑指 Offer II 049. 从根节点到叶节点的路径数字之和 | 递归(深搜):二叉树的前序遍历(⭐) | |
| 剑指 Offer II 050. 向下的路径节点值之和 | 递归(深搜):二叉树的前序遍历(⭐) | |
| 剑指 Offer II 051. 节点值之和最大的路径 | 递归(深搜):二叉树的后序遍历(⭐) | |
| 二叉搜索树 | 剑指 Offer II 052. 展平二叉搜索树 | 栈 + 二叉树中序遍历(⭐) |
| 剑指 Offer II 053. 二叉搜索树的下一个节点 | 中序遍历:二叉搜索树的特性(⭐) | |
| 剑指 Offer II 054. 所有大于或等于节点的值之和 | 倒序的中序遍历(⭐) | |
| 剑指 Offer II 055. 二叉搜索树迭代器 | 二叉搜索树中序遍历:过程拆分(⭐) | |
| 剑指 Offer II 056. 二叉搜索树中两个节点的值之和 | 中序遍历 + HashSet(⭐) | |
| TreeSet 和 TreeMap 的应用 | 剑指 Offer II 057. 值和下标之差都在给定的范围内 | 平衡二叉搜索树:TreeSet (⭐) |
| 剑指 Offer II 058. 日程表 | 平衡二叉搜索树:TreeMap(⭐) |
二叉树是一种典型的具有递归性质的数据结构,二叉树的广度优先搜索通常是通过
队列来实现,而二叉树的深度优先搜索可以分为中序遍历、前序遍历和后序遍历。二叉树 DFS 的递归写法非常简单,按照遍历的顺序编写递归顺序即可;而将递归代码改写成迭代的形式则通常需要栈来辅助,栈主要用于二叉树左侧节点的存储。
……
而二叉搜索树则是一种特殊的二叉树,在二叉搜索树中进行搜索、添加和删除操作的平均时间复杂度均为 O(logn),如果按照中序遍历的顺序遍历一棵二叉搜索树,那么则会按照从小到大的顺序依次遍历每个节点。由于这个特性,与二叉搜索树相关的很多面试题都适合使用中序遍历解决。
……
在 Java 中提供了TreeSet和TreeMap这两种实现了平衡二叉搜索树的数据结构,如果需要动态地在一个排序的数据集合中添加元素,或者需要根据数据的大小查找,那么可以使用 TreeSet 和 TreeMap 解决。
常用方法:
ceiling():返回键大于或等于给定值的最小键;如果没有则返回 nullfloor():返回键小于或等于给定值的最大键;如果没有则返回 nullhigher():返回键大于给定值的最小键;如果没有则返回 nulllower():返回键小于给定值的最大键;如果没有则返回 null
1. 剑指 Offer II 047. 二叉树剪枝 – P132
给定一个二叉树 根节点
root,树的每个节点的值要么是0,要么是1。请剪除该二叉树中所有节点的值为 0 的子树。
节点node的子树为node本身,以及所有node的后代。

1.1 递归(深搜):二叉树的后序遍历 – O(n)(⭐)
时间复杂度 O ( n ) O(n) O(n),空间复杂度 O ( h ) O(h) O(h)
Key:二叉树的后序遍历(左、右、根),一般我们可以用递归(深搜)来实现二叉树的遍历,适用于小树深的情况;而如果是大树深,则更推荐采用迭代的方式(用栈来保存路径)。
/*** 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 {// Solution1:二叉树后序遍历public TreeNode pruneTree(TreeNode root) {if (root == null) return null;root.left = pruneTree(root.left);root.right = pruneTree(root.right);if (root.left == null && root.right == null && root.val == 0) {return null; // 将左、右子节点为null, 且自身值为0的节点剪枝}return root;}
}

2. 剑指 Offer II 048. 序列化和反序列化二叉树 – P134
序列化是将一个数据结构或者对象转换为连续的比特位的操作,进而可以将转换后的数据存储在一个文件或者内存中,同时也可以通过网络传输到另一个计算机环境,采取相反方式重构得到原数据。
……
请设计一个算法来实现二叉树的序列化与反序列化。这里不限定你的序列 / 反序列化算法执行逻辑,只需要保证一个二叉树可以被序列化为一个字符串并且将这个字符串反序列化为原始的树结构。

2.1 递归(深搜):二叉树的前序遍历 – O(n)(⭐)
时间复杂度 O ( n ) O(n) O(n),空间复杂度 O ( h ) O(h) O(h)
如何将二叉树序列化成一个字符串:
- 前序遍历每一个节点;
- 用分隔符对不同的节点进行分隔;
- 把 null 节点序列化成一个特殊的字符串,如 ”#“;
……
反序列化则是:
- 将序列化形成的字符串按分隔符进行分割;
- ”#“ 与 null 对应;
- 递归还原节点;
……
细节:方法传参时,传递的是一个只有一个元素的数组,来作为字符串遍历的下标。
/*** Definition for a binary tree node.* public class TreeNode {* int val;* TreeNode left;* TreeNode right;* TreeNode(int x) { val = x; }* }*/
public class Codec {// Encodes a tree to a single string.public String serialize(TreeNode root) {if (root == null) return "#";String left = serialize(root.left);String right = serialize(root.right);return String.valueOf(root.val)+ "," + left + "," + right;}// Decodes your encoded data to tree.public TreeNode deserialize(String data) {String[] strs = data.split(",");int[] i = {0}; // 细节,变量传参不会改变值,因此要用数组传参return dfs(strs, i);}public TreeNode dfs(String[] strs, int[] i) {String str = strs[i[0]];i[0]++;if (str.equals("#")) return null;TreeNode node = new TreeNode(Integer.parseInt(str));node.left = dfs(strs, i);node.right = dfs(strs, i);return node;}
}// Your Codec object will be instantiated and called as such:
// Codec ser = new Codec();
// Codec deser = new Codec();
// TreeNode ans = deser.deserialize(ser.serialize(root));

3. 剑指 Offer II 049. 从根节点到叶节点的路径数字之和 – P136
给定一个二叉树的根节点
root,树中每个节点都存放有一个0到9之间的数字。
每条从根节点到叶节点的路径都代表一个数字:
- 例如,从根节点到叶节点的路径
1 -> 2 -> 3表示数字123。计算从根节点到叶节点生成的 所有数字之和 。
叶节点 是指没有子节点的节点。

3.1 递归(深搜):二叉树的前序遍历 – O(n)(⭐)
时间复杂度 O ( n ) O(n) O(n),空间复杂度 O ( h ) O(h) O(h)
Key:二叉树中路径相关的面试题,通常都可以使用深度优先搜索的方法解决,这是因为路径通常顺着指向子节点的指针的方向,也就是纵向方法。
/*** 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 {// Solution1:二叉树前序遍历记录路径public int sumNumbers(TreeNode root) {return dfs(root, 0);}public int dfs(TreeNode root, int path) {if (root == null) return 0;path = path * 10 + root.val; // 记录路径if (root.left == null && root.right == null) return path;return dfs(root.left, path) + dfs(root.right, path);}
}

4. 剑指 Offer II 050. 向下的路径节点值之和 – P136
给定一个二叉树的根节点
root,和一个整数targetSum,求该二叉树里节点值之和等于targetSum的 路径 的数目。
路径 不需要从根节点开始,也不需要在叶子节点结束,但是路径方向必须是向下的(只能从父节点到子节点)。

4.1 递归(深搜):二叉树的前序遍历 – O(n)(⭐)
时间复杂度 O ( n ) O(n) O(n),空间复杂度 O ( h ) O(h) O(h)
Key:path 用于记录从根节点出发所经过的路径,map 用于记录累加路径和出现的次数,遍历所有的节点,在这个过程中 path - target 在map 中出现的次数即为二叉树中节点值之和等于 target 的路径的数目。
/*** 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 pathSum(TreeNode root, int targetSum) {Map<Long, Integer> map = new HashMap<>(); // Node.val <= 109, 相加有可能数值溢出map.put(0L, 1); // 赋初值,path - target return dfs(root, targetSum, map, 0);}public int dfs(TreeNode root, int target, Map<Long, Integer> map, long path) {if (root == null) return 0;path += root.val;int count = map.getOrDefault(path-target, 0); // 查询当前路径下是否存在符合条件的子路径map.put(path, map.getOrDefault(path,0)+1); // 记录当前路径count += dfs(root.left, target, map, path); // 向左遍历count += dfs(root.right, target, map, path); // 向右遍历map.put(path, map.get(path)-1); // 删除当前路径节点,回到上一路径状态return count;}
}

5. 剑指 Offer II 051. 节点值之和最大的路径 – P139
路径 被定义为一条从树中任意节点出发,沿父节点-子节点连接,达到任意节点的序列。同一个节点在一条路径序列中 至多出现一次 。该路径 至少包含一个 节点,且不一定经过根节点。
路径和 是路径中各节点值的总和。
给定一个二叉树的根节点root,返回其 最大路径和,即所有路径上节点值之和的最大值。

5.1 递归(深搜):二叉树的后序遍历 – O(n)(⭐)
时间复杂度 O ( n ) O(n) O(n),空间复杂度 O ( h ) O(h) O(h)
Key:原书题解太过麻烦,推荐 【灵茶山艾府】彻底掌握直径 DP!从二叉树到一般树!(Python/Java/C++/Go),重点是要理解递归思想,递归就像是一个后入先出的栈,一层层递归返回。
/*** 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 res = Integer.MIN_VALUE;public int maxPathSum(TreeNode root) {dfs(root);return res;}public int dfs(TreeNode root) {if (root == null) return 0;int left = dfs(root.left); // 左子树最大链和int right = dfs(root.right); // 右子树最大链和res = Math.max(res, left + right + root.val); // 两条链拼成路径return Math.max(Math.max(left, right) + root.val, 0); // 路径和为负数,则不选}
}

6. 剑指 Offer II 052. 展平二叉搜索树 – P142
给你一棵二叉搜索树,请 按中序遍历 将其重新排列为一棵递增顺序搜索树,使树中最左边的节点成为树的根节点,并且每个节点没有左子节点,只有一个右子节点。

6.1 栈 + 二叉树中序遍历 – O(n)(⭐)
时间复杂度 O ( n ) O(n) O(n),空间复杂度 O ( n ) O(n) O(n)
以中序遍历的顺序,递增式的遍历每个节点,并在遍历的过程中改变节点的指向。
/*** 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 {// Solution1:二叉树中序遍历public TreeNode increasingBST(TreeNode root) {Stack<TreeNode> sk = new Stack<>();TreeNode cur = root; // 当前节点TreeNode prev = null; // 前一节点TreeNode head = null; // 展开后的首节点while (cur != null || !sk.isEmpty()) {while (cur != null) { // 将以当前节点为首的左侧节点全部入栈sk.push(cur);cur = cur.left;}cur = sk.pop(); // 左侧节点以此出栈if (prev != null) { prev.right = cur;} else {head = cur; // 如果prev为null,说明此时出栈的节点是展开后的头节点}prev = cur;cur.left = null;cur = cur.right; // 继续遍历}return head;}
}

7. 剑指 Offer II 053. 二叉搜索树的下一个节点 – P144
给定一棵二叉搜索树和其中的一个节点
p,找到该节点在树中的中序后继。如果节点没有中序后继,请返回null。
节点p的后继是值比p.val大的节点中键值最小的节点,即按中序遍历的顺序节点p的下一个节点。

7.1 中序遍历:二叉搜索树的特性 – O(h)(⭐)
时间复杂度 O ( h ) O(h) O(h),空间复杂度 O ( 1 ) O(1) O(1)
该题最直观的思路就是采用二叉树的中序遍历,逐一遍历二叉树的每个节点。但由于本题是二叉搜索树,因此我们可以利用二叉搜索树的特性,每当到达一个节点就比较根节点的值和节点 p 的值,如果当前节点的值比节点 p 的值大,我们就暂存其节点,并向其左孩子遍历;如果当前节点的值比节点 p 的值要小,那么我们就向其右孩子遍历,直至找到所有比 p 节点值大的节点中值最小的那一个。
/*** Definition for a binary tree node.* public class TreeNode {* int val;* TreeNode left;* TreeNode right;* TreeNode(int x) { val = x; }* }*/
class Solution {public TreeNode inorderSuccessor(TreeNode root, TreeNode p) {TreeNode cur = root;TreeNode res = null; // 返回结果while (cur != null) {if (cur.val > p.val) {res = cur;cur = cur.left; // 当前节点值大于p节点值,暂存当前节点,并寻找大于p节点值中最小的节点} else {cur = cur.right;}}return res; }
}

8. 剑指 Offer II 054. 所有大于或等于节点的值之和 – P146
给定一个二叉搜索树,请将它的每个节点的值替换成树中大于或者等于该节点值的所有节点值之和。
提醒一下,二叉搜索树满足下列约束条件:
- 节点的左子树仅包含键 小于 节点键的节点。
- 节点的右子树仅包含键 大于 节点键的节点。
- 左右子树也必须是二叉搜索树。

8.1 倒序的中序遍历 – O(n)(⭐)
时间复杂度 O ( n ) O(n) O(n),空间复杂度 O ( h ) O(h) O(h)
Key:二叉搜索树的中序遍历按照节点值从小到大按顺序遍历,也就是当遍历到某个节点时比该节点小的节点都已经遍历过了,而本题要求的是将每个节点的值替换成树中大于或者等于该节点值的所有节点值之和,因此可以倒序中序遍历,从右 根 左的顺序,从大到小进行遍历,这样就保证了只需一次遍历即可求出节点值的和。(当然了我们也可以通过两次遍历,第一次先求出所有节点值的和,第二次依次遍历用 total - sum 得到需要的值)
/*** 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 {// Soultion1:倒序的中序遍历(右 根 左)public TreeNode convertBST(TreeNode root) {Stack<TreeNode> sk = new Stack<>();TreeNode cur = root;int sum = 0;while (cur != null || !sk.isEmpty()) {while (cur != null) {sk.push(cur);cur = cur.right;}cur = sk.pop();sum += cur.val;cur.val = sum;cur = cur.left;}return root;}
}

9. 剑指 Offer II 055. 二叉搜索树迭代器 – P148
实现一个二叉搜索树迭代器类
BSTIterator,表示一个按中序遍历二叉搜索树(BST)的迭代器:
BSTIterator(TreeNode root)初始化BSTIterator类的一个对象。BST 的根节点root会作为构造函数的一部分给出。指针应初始化为一个不存在于 BST 中的数字,且该数字小于 BST 中的任何元素。boolean hasNext()如果向指针右侧遍历存在数字,则返回true;否则返回false。int next()将指针向右移动,然后返回指针处的数字。……
注意,指针初始化为一个不存在于 BST 中的数字,所以对next()的首次调用将返回 BST 中的最小元素。
9.1 二叉搜索树中序遍历:过程拆分 – O(1)(⭐)
时间复杂度 O ( 1 ) O(1) O(1),空间复杂度 O ( h ) O(h) O(h)
该题根据不同的限制条件有不同的解法,如:
- 可以改变输入的二叉树结构:那么我们就可以将二叉搜索树展平,相当于对链表的遍历;
- 如果不可以改变输入的二叉树结构:那么我们可以在二叉搜索树遍历的时候用链表将节点记录下来,这样的缺点就是需要使用额外的存储结构;
- 如果不可以改变输入的二叉树结构,且不能使用额外的存储空间:拆分二叉搜素树中序遍历的过程也是可以的,我们需要理解 二叉搜索树中序遍历 本身的外循环判断条件就可以作为 hasNext 的判断,然后通过 next ,每调用一次,进行一次循环遍历即可。
/*** 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 BSTIterator {TreeNode cur;Stack<TreeNode> sk;public BSTIterator(TreeNode root) { // 构造函数:输入二叉搜索树的根节点初始化该迭代器cur = root;sk = new Stack<>();}public int next() { // 返回二叉搜索树中下一个最小的节点的值while (cur != null) {sk.push(cur);cur = cur.left;}cur = sk.pop();int val = cur.val;cur = cur.right;return val;}public boolean hasNext() { // 返回二叉搜索树是否还有下一个节点return cur != null || !sk.isEmpty();}
}/*** Your BSTIterator object will be instantiated and called as such:* BSTIterator obj = new BSTIterator(root);* int param_1 = obj.next();* boolean param_2 = obj.hasNext();*/

10. 剑指 Offer II 056. 二叉搜索树中两个节点的值之和 – P150
给定一个二叉搜索树的 根节点
root和一个整数k, 请判断该二叉搜索树中是否存在两个节点它们的值之和等于k。假设二叉搜索树中节点的值均唯一。

10.1 中序遍历 + HashSet – O(n)(⭐)
时间复杂度 O ( n ) O(n) O(n),空间复杂度 O ( n ) O(n) O(n)
Key:该题可以看成二叉树版的两数之和,常规做法就是通过顺序遍历,找出该数据结构中的数据是否存在
target - cur.val,如果存在,则说明该数据结构中存在两数之和等于 target,后序进阶可能会要求输出符合条件的两数的下标值,改用 HashMap 在记录值的同时存储下标即可解决。
……
Ps:当然以上做法并未考虑二叉搜索树这一要素,如想进一步优化,我们可以利用二叉搜素树的特点,将时间复杂度降低至 O(h),通过数组双指针的策略来进行数据的遍历,但需要编写专门的二叉搜索树迭代器(正序和倒序都需要)。
/*** 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 findTarget(TreeNode root, int k) {Set<Integer> st = new HashSet<>();Deque<TreeNode> sk = new ArrayDeque<>(); // stack换成Deque可以提高速度TreeNode cur = root;while (cur != null || !sk.isEmpty()) { // 中序遍历while (cur != null) {sk.push(cur);cur = cur.left;}cur = sk.pop();if (st.contains(k-cur.val)) { // 常规套路(两数之和)return true;}st.add(cur.val); // 记录每一次遍历到的值cur = cur.right;} return false;}
}

11. 剑指 Offer II 057. 值和下标之差都在给定的范围内 – P155
给你一个整数数组
nums和两个整数k和t。请你判断是否存在 两个不同下标i和j,使得abs(nums[i] - nums[j]) <= t,同时又满足abs(i - j) <= k。
如果存在则返回true,不存在返回false。

11.1 平衡二叉搜索树:TreeSet – O(nlogn)(⭐)
时间复杂度 O ( n l o g n ) O(nlogn) O(nlogn),空间复杂度 O ( n ) O(n) O(n)
Key:该题最常规的做法就是遍历所有数字,然后每遍历到一个数字就判断该数字的前 k 个数字与其差的绝对值是否小于等于 t,这种方法的时间复杂度为 O(nk)。
……
除以上方法外,还有一种巧妙的方法可以时间复杂度降低至 O(n),此时我们需要借助 HashMap,并通过给遍历的每个数字划分桶号(这里我们可以理解为数据范围,0-t 范围入 0 桶,t+1 - 2t+1范围入 1 桶,以此类推),这样我们只要找到一个桶中有两个数即可判断是否有数字满足条件;如果没有,我们还可以查看当前数字所在桶的前后两桶中是否有数字可以和当前数字组合满足条件,循环遍历一遍即可解决问题。
……
本题下方给出的代码属于另一种方法,属于对 TreeSet 性质的运用,通过 TreeSet 我们可以以 O(logn) 的时间复杂度得出一个数字对应集合中 ≥的最小值,以及 ≤的最大值,对这两个数字进行判断,即可得出当前数字与其前 k 个数字之间的一个数据范围,然后得出答案。
class Solution {public boolean containsNearbyAlmostDuplicate(int[] nums, int k, int t) {TreeSet<Long> set = new TreeSet<>(); // 注意包装类应选择Long,int有溢出风险for (int i = 0; i < nums.length; i++) {Long lower = set.floor((long)nums[i]); // 下取值,找小最大if (lower != null && nums[i] - lower <= t) return true;Long higher = set.ceiling((long)nums[i]); // 上取值,找大最小if (higher != null && higher - nums[i] <= t) return true;set.add((long) nums[i]);if (i >= k) { // 移除多余数值,set只保留当前数字的前k个数值set.remove((long)nums[i-k]);}}return false;}
}

12. 剑指 Offer II 058. 日程表 – P157
请实现一个
MyCalendar类来存放你的日程安排。如果要添加的时间内没有其他安排,则可以存储这个新的日程安排。
MyCalendar有一个book(int start, int end)方法。它意味着在start到end时间内增加一个日程安排,注意,这里的时间是半开区间,即[start, end), 实数x的范围为,start <= x < end。
当两个日程安排有一些时间上的交叉时(例如两个日程安排都在同一时间内),就会产生重复预订。
每次调用MyCalendar.book方法时,如果可以将日程安排成功添加到日历中而不会导致重复预订,返回true。否则,返回false并且不要将该日程安排添加到日历中。
12.1 平衡二叉搜索树:TreeMap – O(nlogn)(⭐)
时间复杂度 O ( n l o g n ) O(nlogn) O(nlogn),空间复杂度 O ( n ) O(n) O(n)
常规方法:直接遍历,通过 ArrayList<int[]> 对数据进行存储,每次 book(start, end) 操作都会对 ArrayList 进行一次遍历,比较当前事件是否与已经存在的事件之间存在冲突,如果冲突返回 false,无冲突则将当前事件加入到集合中,并返回 true。
……
利用二叉搜索树的特性:使用 TreeSet 或 TreeMap 对数据进行存储,利用其特有的 API — floorEntry() / ceilEntry() 找出小最大和大最小,然后与当前事件的时间范围进行比较即可,节省了比较次数。
……
还可以使用线段树的方法,详细内容可参考:方法三:线段树
TreeMap 实现的接口、继承的类,如下图所示:

class MyCalendar {TreeMap<Integer,Integer> map; // 写成 Map 不行,会出现 error: cannot find symbol [in MyCalendar.java] 错误,这是因为其中的floorEntry()以及ceilingEntry()方法是源自于public interface NavigableMap<K,V>接口的。public MyCalendar() {map = new TreeMap<>();}public boolean book(int start, int end) {Map.Entry<Integer,Integer> event = map.floorEntry(start); // 下取值,找到比当前开始时间低的最近的开始事件if (event != null && event.getValue() > start) return false; // 判断找到的事件的结束时间是否超过了当前的开始时间event = map.ceilingEntry(start); // 上取值,找到比当前开始时间高的最近开始事件if (event != null && event.getKey() < end) return false; // 判断找到的事件的开始时间是否早于当前的结束时间map.put(start, end); // 都没有,则说明当前事件符合要求,可以放入集合return true;}
}
/*** Your MyCalendar object will be instantiated and called as such:* MyCalendar obj = new MyCalendar();* boolean param_1 = obj.book(start,end);*/

13. 继续提升:加练题目
🎈 可参考:
- 树 · SharingSource/LogicStack-LeetCode Wiki · GitHub
- 二叉树 · SharingSource/LogicStack-LeetCode Wiki · GitHub
- 线段树 · SharingSource/LogicStack-LeetCode Wiki · GitHub
相关文章:
【LeetCode】剑指 Offer Ⅱ 第8章:树(12道题) -- Java Version
题库链接:https://leetcode.cn/problem-list/e8X3pBZi/ 类型题目解决方案二叉树的深搜剑指 Offer II 047. 二叉树剪枝递归(深搜):二叉树的后序遍历 (⭐)剑指 Offer II 048. 序列化和反序列化二叉树递归&…...
利用maven的dependency插件将项目依赖从maven仓库中拷贝到一个指定的位置
https://maven.apache.org/plugins/maven-dependency-plugin/copy-dependencies-mojo.html 利用dependency:copy-dependencies可以将项目的依赖从maven仓库中拷贝到一个指定的位置。 使用默认配置拷贝依赖 如果直接执行mvn dependency:copy-dependencies,是将项目…...
在Flask中实现文件上传七牛云中并下载
在Flask中实现文件上传和七牛云集成 文件上传是Web应用中常见的功能之一,而七牛云则提供了强大的云存储服务,使得文件存储和管理变得更加便捷。在本篇博客中,我们将学习如何在Flask应用中实现文件上传,并将上传的文件保存到七牛云…...
【Linux】centOS7安装配置及Linux的常用命令---超详细
一,centOS 1.1 centOS的概念 CentOS(Community Enterprise Operating System)是一个由社区支持的企业级操作系统,它是以Red Hat Enterprise Linux(RHEL)源代码为基础构建的。CentOS提供了一个稳定、可靠且…...
【ES专题】ElasticSearch搜索进阶
目录 前言阅读导航前置知识特别提醒笔记正文一、分词器详解1.1 基本概念1.2 分词发生的时期1.3 分词器的组成1.3.1 切词器:Tokenizer1.3.2 词项过滤器:Token Filter1.3.3 字符过滤器:Character Filter 1.4 倒排索引的数据结构 <font color…...
【iOS免越狱】利用IOS自动化WebDriverAgent实现自动直播间自动输入
1.目标 由于看直播的时候主播叫我发 666,支持他,我肯定支持他呀,就一直发,可是后来发现太浪费时间了,能不能做一个直播间自动发 666 呢?于是就开始下面的操作。 2.操作环境 iPhone一台 WebDriverAgent …...
Python基础入门例程28-NP28 密码游戏(列表)
最近的博文: Python基础入门例程27-NP27 朋友们的喜好(列表)-CSDN博客 Python基础入门例程26-NP26 牛牛的反转列表(列表)-CSDN博客 Python基础入门例程25-NP25 有序的列表(列表)-CSDN博客 目录…...
乌班图 Linux 系统 Ubuntu 23.10.1 发布更新镜像
Ubuntu 团队在其官网上发布了Ubuntu 23.10.1 版本,这是目前较新的 Ubuntu 23.10(Focal Fossa)操作系统系列的第一个发行版,旨在为社区提供最新的安装媒体。Ubuntu 22.04 LTS(Focal Fossa)操作系统系列于 2022 年 4 月 21 日发布。 Ubuntu 23.10 LTS(长期支持版本)可用…...
Java金字塔、空心金字塔、空心菱形
Java金字塔 public class TestDemo01 {public static void main(String[] args){//第一个for用于每行输出 从i1开始到i<5,总共5行for(int i1;i<5;i){//每行前缀空格,这个for用于表示每行输出*前面的空格//从上面规律可得,每行输出的空格数为总层数,…...
前端 | (十四)canvas基本用法 | 尚硅谷前端HTML5教程(html5入门经典)
文章目录 📚canvas基本用法🐇什么是canvas(画布)🐇替换内容🐇canvas标签的两个属性🐇渲染上下文 📚绘制矩形🐇绘制矩形🐇strokeRect时,边框像素渲染问题🐇添加…...
206.反转链表
206.反转链表 力扣题目链接(opens new window) 题意:反转一个单链表。 示例: 输入: 1->2->3->4->5->NULL 输出: 5->4->3->2->1->NULL 双双指针法: 创建三个节点 pre(反转时的第一个节点)、cur(当前指向需要反转的节点…...
SpringBoot项目从resources目录读取文件
SpringBoot 从 resources 读取文件 使用 Spring 给我们提供的工具类来进行读取 File file org.springframework.util.ResourceUtils.getFile("classpath:人物模板.docx");可能读取失败,出现如下错误: java.io.FileNotFoundException: clas…...
SQL实现根据时间戳和增量标记IDU获取最新记录和脱IDU标记
需求说明:表中有 id, info, cnt 三个字段,对应的增量表多idu增量标记字段和时间戳字段ctimestamp。增量表中的 id 会有重复,其他字段 info、cnt 会不断更新,idu为增量标记字段,ctimestamp为IDU操作的时间戳。目的时要做…...
京东数据平台:2023年9月京东智能家居行业数据分析
鲸参谋监测的京东平台9月份智能家居市场销售数据已出炉! 9月份,智能家居市场销售额有小幅上涨。根据鲸参谋电商数据分析平台的相关数据显示,今年9月,京东平台智能家居的销量为37万,销售额将近8300万,同比增…...
计算两个时间之间连续的日期(java)
背景介绍 给出两个时间,希望算出两者之间连续的日期,比如时间A:2023-10-01 00:00:00 时间B:2023-11-30 23:59:59,期望得到的连续日期为2023-10-01、2023-10-02、… 2023-11-30 Java版代码示例 import java.time.temporal.ChronoUnit; import java.tim…...
Kali Linux:网络与安全专家的终极武器
文章目录 一、Kali Linux 简介二、Kali Linux 的优势三、使用 Kali Linux 进行安全任务推荐阅读 ——《Kali Linux高级渗透测试》适读人群内容简介作者简介目录 Kali Linux:网络与安全专家的终极武器 Kali Linux,对于许多网络和安全专业人士来说&#x…...
Leetcode—101.对称二叉树【简单】
2023每日刷题(十九) Leetcode—101.对称二叉树 利用Leetcode101.对称二叉树的思想的实现代码 /*** Definition for a binary tree node.* struct TreeNode {* int val;* struct TreeNode *left;* struct TreeNode *right;* };*/ bool isSa…...
判断是否工作在docker环境
判断是否工作在docker环境 方式一:判断根目录下 .dockerenv 文件 docker环境下:ls -alh /.dockerenv , 非docker环境,没有这个.dockerenv文件的 注:定制化比较高的docker系统也可能没有这个文件 方式二:查询系统进程…...
文件上传学习笔记
笔记 文件上传 文件上传是指将本地图片,视频,音频等文件上传到服务器,供其它用户浏览或下载的过程 文件上传前端三要素 : file表单项 post方式 multipart/from-data 服务端接收文件 : 用spring中的API : MultipartFile 要想文件名唯一 …...
【GitLab CI/CD、SpringBoot、Docker】GitLab CI/CD 部署SpringBoot应用,部署方式Docker
介绍 本文件主要介绍如何将SpringBoot应用使用Docker方式部署,并用Gitlab CI/CD进行构建和部署。 环境准备 已安装Gitlab仓库已安装Gitlab Runner,并已注册到Gitlab和已实现基础的CI/CD使用创建Docker Hub仓库,教程中使用的是阿里云的Docker…...
23-Oracle 23 ai 区块链表(Blockchain Table)
小伙伴有没有在金融强合规的领域中遇见,必须要保持数据不可变,管理员都无法修改和留痕的要求。比如医疗的电子病历中,影像检查检验结果不可篡改行的,药品追溯过程中数据只可插入无法删除的特性需求;登录日志、修改日志…...
dedecms 织梦自定义表单留言增加ajax验证码功能
增加ajax功能模块,用户不点击提交按钮,只要输入框失去焦点,就会提前提示验证码是否正确。 一,模板上增加验证码 <input name"vdcode"id"vdcode" placeholder"请输入验证码" type"text&quo…...
P3 QT项目----记事本(3.8)
3.8 记事本项目总结 项目源码 1.main.cpp #include "widget.h" #include <QApplication> int main(int argc, char *argv[]) {QApplication a(argc, argv);Widget w;w.show();return a.exec(); } 2.widget.cpp #include "widget.h" #include &q…...
Neo4j 集群管理:原理、技术与最佳实践深度解析
Neo4j 的集群技术是其企业级高可用性、可扩展性和容错能力的核心。通过深入分析官方文档,本文将系统阐述其集群管理的核心原理、关键技术、实用技巧和行业最佳实践。 Neo4j 的 Causal Clustering 架构提供了一个强大而灵活的基石,用于构建高可用、可扩展且一致的图数据库服务…...
涂鸦T5AI手搓语音、emoji、otto机器人从入门到实战
“🤖手搓TuyaAI语音指令 😍秒变表情包大师,让萌系Otto机器人🔥玩出智能新花样!开整!” 🤖 Otto机器人 → 直接点明主体 手搓TuyaAI语音 → 强调 自主编程/自定义 语音控制(TuyaAI…...
Springboot社区养老保险系统小程序
一、前言 随着我国经济迅速发展,人们对手机的需求越来越大,各种手机软件也都在被广泛应用,但是对于手机进行数据信息管理,对于手机的各种软件也是备受用户的喜爱,社区养老保险系统小程序被用户普遍使用,为方…...
使用Spring AI和MCP协议构建图片搜索服务
目录 使用Spring AI和MCP协议构建图片搜索服务 引言 技术栈概览 项目架构设计 架构图 服务端开发 1. 创建Spring Boot项目 2. 实现图片搜索工具 3. 配置传输模式 Stdio模式(本地调用) SSE模式(远程调用) 4. 注册工具提…...
【Android】Android 开发 ADB 常用指令
查看当前连接的设备 adb devices 连接设备 adb connect 设备IP 断开已连接的设备 adb disconnect 设备IP 安装应用 adb install 安装包的路径 卸载应用 adb uninstall 应用包名 查看已安装的应用包名 adb shell pm list packages 查看已安装的第三方应用包名 adb shell pm list…...
为什么要创建 Vue 实例
核心原因:Vue 需要一个「控制中心」来驱动整个应用 你可以把 Vue 实例想象成你应用的**「大脑」或「引擎」。它负责协调模板、数据、逻辑和行为,将它们变成一个活的、可交互的应用**。没有这个实例,你的代码只是一堆静态的 HTML、JavaScript 变量和函数,无法「活」起来。 …...
协议转换利器,profinet转ethercat网关的两大派系,各有千秋
随着工业以太网的发展,其高效、便捷、协议开放、易于冗余等诸多优点,被越来越多的工业现场所采用。西门子SIMATIC S7-1200/1500系列PLC集成有Profinet接口,具有实时性、开放性,使用TCP/IP和IT标准,符合基于工业以太网的…...
