当前位置: 首页 > news >正文

【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 中提供了 TreeSetTreeMap 这两种实现了平衡二叉搜索树的数据结构,如果需要动态地在一个排序的数据集合中添加元素,或者需要根据数据的大小查找,那么可以使用 TreeSet 和 TreeMap 解决。
常用方法:

  • ceiling():返回键大于或等于给定值的最小键;如果没有则返回 null
  • floor():返回键小于或等于给定值的最大键;如果没有则返回 null
  • higher():返回键大于给定值的最小键;如果没有则返回 null
  • lower():返回键小于给定值的最大键;如果没有则返回 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)

如何将二叉树序列化成一个字符串:

  1. 前序遍历每一个节点;
  2. 用分隔符对不同的节点进行分隔;
  3. 把 null 节点序列化成一个特殊的字符串,如 ”#“;

……
反序列化则是:

  1. 将序列化形成的字符串按分隔符进行分割;
  2. ”#“ 与 null 对应;
  3. 递归还原节点;

……
细节:方法传参时,传递的是一个只有一个元素的数组,来作为字符串遍历的下标。

/*** 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 ,树中每个节点都存放有一个 09 之间的数字。
每条从根节点到叶节点的路径都代表一个数字:

  • 例如,从根节点到叶节点的路径 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)

该题根据不同的限制条件有不同的解法,如:

  1. 可以改变输入的二叉树结构:那么我们就可以将二叉搜索树展平,相当于对链表的遍历;
  2. 如果不可以改变输入的二叉树结构:那么我们可以在二叉搜索树遍历的时候用链表将节点记录下来,这样的缺点就是需要使用额外的存储结构;
  3. 如果不可以改变输入的二叉树结构,且不能使用额外的存储空间:拆分二叉搜素树中序遍历的过程也是可以的,我们需要理解 二叉搜索树中序遍历 本身的外循环判断条件就可以作为 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 和两个整数 kt 。请你判断是否存在 两个不同下标 ij,使得 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)方法。它意味着在 startend 时间内增加一个日程安排,注意,这里的时间是半开区间,即 [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

题库链接&#xff1a;https://leetcode.cn/problem-list/e8X3pBZi/ 类型题目解决方案二叉树的深搜剑指 Offer II 047. 二叉树剪枝递归&#xff08;深搜&#xff09;&#xff1a;二叉树的后序遍历 &#xff08;⭐&#xff09;剑指 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&#xff0c;是将项目…...

在Flask中实现文件上传七牛云中并下载

在Flask中实现文件上传和七牛云集成 文件上传是Web应用中常见的功能之一&#xff0c;而七牛云则提供了强大的云存储服务&#xff0c;使得文件存储和管理变得更加便捷。在本篇博客中&#xff0c;我们将学习如何在Flask应用中实现文件上传&#xff0c;并将上传的文件保存到七牛云…...

【Linux】centOS7安装配置及Linux的常用命令---超详细

一&#xff0c;centOS 1.1 centOS的概念 CentOS&#xff08;Community Enterprise Operating System&#xff09;是一个由社区支持的企业级操作系统&#xff0c;它是以Red Hat Enterprise Linux&#xff08;RHEL&#xff09;源代码为基础构建的。CentOS提供了一个稳定、可靠且…...

【ES专题】ElasticSearch搜索进阶

目录 前言阅读导航前置知识特别提醒笔记正文一、分词器详解1.1 基本概念1.2 分词发生的时期1.3 分词器的组成1.3.1 切词器&#xff1a;Tokenizer1.3.2 词项过滤器&#xff1a;Token Filter1.3.3 字符过滤器&#xff1a;Character Filter 1.4 倒排索引的数据结构 <font color…...

【iOS免越狱】利用IOS自动化WebDriverAgent实现自动直播间自动输入

1.目标 由于看直播的时候主播叫我发 666&#xff0c;支持他&#xff0c;我肯定支持他呀&#xff0c;就一直发&#xff0c;可是后来发现太浪费时间了&#xff0c;能不能做一个直播间自动发 666 呢&#xff1f;于是就开始下面的操作。 2.操作环境 iPhone一台 WebDriverAgent …...

Python基础入门例程28-NP28 密码游戏(列表)

最近的博文&#xff1a; Python基础入门例程27-NP27 朋友们的喜好&#xff08;列表&#xff09;-CSDN博客 Python基础入门例程26-NP26 牛牛的反转列表&#xff08;列表&#xff09;-CSDN博客 Python基础入门例程25-NP25 有序的列表&#xff08;列表&#xff09;-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){//每行前缀空格&#xff0c;这个for用于表示每行输出*前面的空格//从上面规律可得,每行输出的空格数为总层数&#xff0c…...

前端 | (十四)canvas基本用法 | 尚硅谷前端HTML5教程(html5入门经典)

文章目录 &#x1f4da;canvas基本用法&#x1f407;什么是canvas(画布)&#x1f407;替换内容&#x1f407;canvas标签的两个属性&#x1f407;渲染上下文 &#x1f4da;绘制矩形&#x1f407;绘制矩形&#x1f407;strokeRect时&#xff0c;边框像素渲染问题&#x1f407;添加…...

206.反转链表

206.反转链表 力扣题目链接(opens new window) 题意&#xff1a;反转一个单链表。 示例: 输入: 1->2->3->4->5->NULL 输出: 5->4->3->2->1->NULL 双双指针法&#xff1a; 创建三个节点 pre(反转时的第一个节点)、cur(当前指向需要反转的节点…...

SpringBoot项目从resources目录读取文件

SpringBoot 从 resources 读取文件 使用 Spring 给我们提供的工具类来进行读取 File file org.springframework.util.ResourceUtils.getFile("classpath:人物模板.docx");可能读取失败&#xff0c;出现如下错误&#xff1a; java.io.FileNotFoundException: clas…...

SQL实现根据时间戳和增量标记IDU获取最新记录和脱IDU标记

需求说明&#xff1a;表中有 id, info, cnt 三个字段&#xff0c;对应的增量表多idu增量标记字段和时间戳字段ctimestamp。增量表中的 id 会有重复&#xff0c;其他字段 info、cnt 会不断更新&#xff0c;idu为增量标记字段&#xff0c;ctimestamp为IDU操作的时间戳。目的时要做…...

京东数据平台:2023年9月京东智能家居行业数据分析

鲸参谋监测的京东平台9月份智能家居市场销售数据已出炉&#xff01; 9月份&#xff0c;智能家居市场销售额有小幅上涨。根据鲸参谋电商数据分析平台的相关数据显示&#xff0c;今年9月&#xff0c;京东平台智能家居的销量为37万&#xff0c;销售额将近8300万&#xff0c;同比增…...

计算两个时间之间连续的日期(java)

背景介绍 给出两个时间&#xff0c;希望算出两者之间连续的日期&#xff0c;比如时间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&#xff1a;网络与安全专家的终极武器 Kali Linux&#xff0c;对于许多网络和安全专业人士来说&#x…...

Leetcode—101.对称二叉树【简单】

2023每日刷题&#xff08;十九&#xff09; Leetcode—101.对称二叉树 利用Leetcode101.对称二叉树的思想的实现代码 /*** Definition for a binary tree node.* struct TreeNode {* int val;* struct TreeNode *left;* struct TreeNode *right;* };*/ bool isSa…...

判断是否工作在docker环境

判断是否工作在docker环境 方式一&#xff1a;判断根目录下 .dockerenv 文件 docker环境下&#xff1a;ls -alh /.dockerenv , 非docker环境&#xff0c;没有这个.dockerenv文件的 注&#xff1a;定制化比较高的docker系统也可能没有这个文件 方式二&#xff1a;查询系统进程…...

文件上传学习笔记

笔记 文件上传 文件上传是指将本地图片&#xff0c;视频&#xff0c;音频等文件上传到服务器&#xff0c;供其它用户浏览或下载的过程 文件上传前端三要素 : file表单项 post方式 multipart/from-data 服务端接收文件 : 用spring中的API : MultipartFile 要想文件名唯一 …...

【GitLab CI/CD、SpringBoot、Docker】GitLab CI/CD 部署SpringBoot应用,部署方式Docker

介绍 本文件主要介绍如何将SpringBoot应用使用Docker方式部署&#xff0c;并用Gitlab CI/CD进行构建和部署。 环境准备 已安装Gitlab仓库已安装Gitlab Runner&#xff0c;并已注册到Gitlab和已实现基础的CI/CD使用创建Docker Hub仓库&#xff0c;教程中使用的是阿里云的Docker…...

React Native 导航系统实战(React Navigation)

导航系统实战&#xff08;React Navigation&#xff09; React Navigation 是 React Native 应用中最常用的导航库之一&#xff0c;它提供了多种导航模式&#xff0c;如堆栈导航&#xff08;Stack Navigator&#xff09;、标签导航&#xff08;Tab Navigator&#xff09;和抽屉…...

Swift 协议扩展精进之路:解决 CoreData 托管实体子类的类型不匹配问题(下)

概述 在 Swift 开发语言中&#xff0c;各位秃头小码农们可以充分利用语法本身所带来的便利去劈荆斩棘。我们还可以恣意利用泛型、协议关联类型和协议扩展来进一步简化和优化我们复杂的代码需求。 不过&#xff0c;在涉及到多个子类派生于基类进行多态模拟的场景下&#xff0c;…...

家政维修平台实战20:权限设计

目录 1 获取工人信息2 搭建工人入口3 权限判断总结 目前我们已经搭建好了基础的用户体系&#xff0c;主要是分成几个表&#xff0c;用户表我们是记录用户的基础信息&#xff0c;包括手机、昵称、头像。而工人和员工各有各的表。那么就有一个问题&#xff0c;不同的角色&#xf…...

成都鼎讯硬核科技!雷达目标与干扰模拟器,以卓越性能制胜电磁频谱战

在现代战争中&#xff0c;电磁频谱已成为继陆、海、空、天之后的 “第五维战场”&#xff0c;雷达作为电磁频谱领域的关键装备&#xff0c;其干扰与抗干扰能力的较量&#xff0c;直接影响着战争的胜负走向。由成都鼎讯科技匠心打造的雷达目标与干扰模拟器&#xff0c;凭借数字射…...

HDFS分布式存储 zookeeper

hadoop介绍 狭义上hadoop是指apache的一款开源软件 用java语言实现开源框架&#xff0c;允许使用简单的变成模型跨计算机对大型集群进行分布式处理&#xff08;1.海量的数据存储 2.海量数据的计算&#xff09;Hadoop核心组件 hdfs&#xff08;分布式文件存储系统&#xff09;&a…...

JVM 内存结构 详解

内存结构 运行时数据区&#xff1a; Java虚拟机在运行Java程序过程中管理的内存区域。 程序计数器&#xff1a; ​ 线程私有&#xff0c;程序控制流的指示器&#xff0c;分支、循环、跳转、异常处理、线程恢复等基础功能都依赖这个计数器完成。 ​ 每个线程都有一个程序计数…...

安宝特案例丨Vuzix AR智能眼镜集成专业软件,助力卢森堡医院药房转型,赢得辉瑞创新奖

在Vuzix M400 AR智能眼镜的助力下&#xff0c;卢森堡罗伯特舒曼医院&#xff08;the Robert Schuman Hospitals, HRS&#xff09;凭借在无菌制剂生产流程中引入增强现实技术&#xff08;AR&#xff09;创新项目&#xff0c;荣获了2024年6月7日由卢森堡医院药剂师协会&#xff0…...

《信号与系统》第 6 章 信号与系统的时域和频域特性

目录 6.0 引言 6.1 傅里叶变换的模和相位表示 6.2 线性时不变系统频率响应的模和相位表示 6.2.1 线性与非线性相位 6.2.2 群时延 6.2.3 对数模和相位图 6.3 理想频率选择性滤波器的时域特性 6.4 非理想滤波器的时域和频域特性讨论 6.5 一阶与二阶连续时间系统 6.5.1 …...

门静脉高压——表现

一、门静脉高压表现 00:01 1. 门静脉构成 00:13 组成结构&#xff1a;由肠系膜上静脉和脾静脉汇合构成&#xff0c;是肝脏血液供应的主要来源。淤血后果&#xff1a;门静脉淤血会同时导致脾静脉和肠系膜上静脉淤血&#xff0c;引发后续系列症状。 2. 脾大和脾功能亢进 00:46 …...

高效的后台管理系统——可进行二次开发

随着互联网技术的迅猛发展&#xff0c;企业的数字化管理变得愈加重要。后台管理系统作为数据存储与业务管理的核心&#xff0c;成为了现代企业不可或缺的一部分。今天我们要介绍的是一款名为 若依后台管理框架 的系统&#xff0c;它不仅支持跨平台应用&#xff0c;还能提供丰富…...