我的日程安排表 II
LeetCode731题,如果出现了第三次区间重叠,则返回false,其余的返回true,该题需要注意细粒度和范围扩大的问题。
实现一个 MyCalendar 类来存放你的日程安排。如果要添加的时间内不会导致三重预订时,则可以存储这个新的日程安排。
MyCalendar 有一个 book(int start, int end)方法。它意味着在 start 到 end 时间内增加一个日程安排,注意,这里的时间是半开区间,即 [start, end), 实数 x 的范围为, start <= x < end。
当三个日程安排有一些时间上的交叉时(例如三个日程安排都在同一时间内),就会产生三重预订。
每次调用 MyCalendar.book方法时,如果可以将日程安排成功添加到日历中而不会导致三重预订,返回 true。否则,返回 false 并且不要将该日程安排添加到日历中。
请按照以下步骤调用MyCalendar 类: MyCalendar cal = new MyCalendar(); MyCalendar.book(start, end)
示例:
MyCalendar();
MyCalendar.book(10, 20); // returns true
MyCalendar.book(50, 60); // returns true
MyCalendar.book(10, 40); // returns true
MyCalendar.book(5, 15); // returns false
MyCalendar.book(5, 10); // returns true
MyCalendar.book(25, 55); // returns true
解释:
前两个日程安排可以添加至日历中。 第三个日程安排会导致双重预订,但可以添加至日历中。
第四个日程安排活动(5,15)不能添加至日历中,因为它会导致三重预订。
第五个日程安排(5,10)可以添加至日历中,因为它未使用已经双重预订的时间10。
第六个日程安排(25,55)可以添加至日历中,因为时间 [25,40] 将和第三个日程安排双重预订;
时间 [40,50] 将单独预订,时间 [50,55)将和第二个日程安排双重预订。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/my-calendar-ii
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
思路分析:
1.维护两个TreeMap,键为区间开始点,值为区间结束点。
2.第一个用来记录只出现了一次的区间,并且注意区间的扩展。
3.第二个用来记录重复出现了两次的区间,当新加的区间在此重叠了,就要直接返回false。
TreeMap<Integer, Integer> singleMap = new TreeMap<>();
static TreeMap<Integer, Integer> doubleMap = new TreeMap<>();
public MyCalendarTwo() {
}
public boolean book(int start, int end) {
//获得小于start的最大key值
Integer a = doubleMap.lowerKey(start);
//区间重叠
if (a != null && doubleMap.get(a) > start) {
return false;
}
//获得大于等于start的最小key值
Integer b = doubleMap.ceilingKey(start);
//区间重叠
if (b != null && (b == start || end > b)) {
return false;
}
a = singleMap.lowerKey(start);
b = singleMap.ceilingKey(start);
//完全不重叠,和两边的都不挨着
if ((a == null || singleMap.get(a) <= start) && (b == null || b >= end)) {
singleMap.put(start, end);
return true;
}
//前区间重叠
if (a != null && singleMap.get(a) > start) {
//单个区间扩展
singleMap.put(a, Math.max(end, singleMap.get(a)));
//重叠部分加入到double中
doubleMap.put(start, Math.min(end, singleMap.get(a)));
//变换需要判断的区间值
start = a;
end = Math.max(end, singleMap.get(a));
}
//后区间重叠,这时候需要判断当前区间,可能包含的不止一个区间了,所以需要判断的值也不止一个了
//可以思考为什么前区间不用循环,这里传入的区间足够大的话是可以包含多个后区间的,但最多只能和一个前区间重合
while (b != null && b < end) {
Integer i = singleMap.get(b);
doubleMap.put(b, Math.min(end, i));
singleMap.remove(b);
singleMap.put(start, Math.max(end, i));
end = Math.max(end, i);
b = singleMap.higherKey(start);
}
return true;
}
车队
LeetCode853题,该题为多辆汽车以不同的出发点和速度行驶在一条路上,当有一辆车追上时,后车就以前车的速度继续行驶,两车形成一个车队。
N 辆车沿着一条车道驶向位于 target 英里之外的共同目的地。
每辆车 i 以恒定的速度 speed[i] (英里/小时),从初始位置 position[i] (英里) 沿车道驶向目的地。
一辆车永远不会超过前面的另一辆车,但它可以追上去,并与前车以相同的速度紧接着行驶。
此时,我们会忽略这两辆车之间的距离,也就是说,它们被假定处于相同的位置。
车队 是一些由行驶在相同位置、具有相同速度的车组成的非空集合。注意,一辆车也可以是一个车队。
即便一辆车在目的地才赶上了一个车队,它们仍然会被视作是同一个车队。
会有多少车队到达目的地?
示例:
输入:target = 12, position = [10,8,0,5,3], speed = [2,4,1,1,3]
输出:3
解释:
从 10 和 8 开始的车会组成一个车队,它们在 12 处相遇。
从 0 处开始的车无法追上其它车,所以它自己就是一个车队。
从 5 和 3 开始的车会组成一个车队,它们在 6 处相遇。
请注意,在到达目的地之前没有其它车会遇到这些车队,所以答案是 3。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/car-fleet
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
思路分析:参考了官方的题解,计算每辆车到达终点时的时间,之后按照离终点的距离进行降序排序,这时候数组第一个的离终点最近,如果他到终点的时间小于下一辆车的话,则下辆车永远追不上,说明他们为两个车队,如果大于的话则能追上,到达终点的时间也是一致的。
class Solution {
public int carFleet(int target, int[] position, int[] speed) {
int N = position.length;
Car[] cars = new Car[N];
for (int i = 0; i < N; ++i)
cars[i] = new Car(position[i], (double) (target - position[i]) / speed[i]);
Arrays.sort(cars, (a, b) -> Integer.compare(a.position, b.position));
int ans = 0, t = N;
while (--t > 0) {
if (cars[t].time < cars[t-1].time) ans++; //if cars[t] arrives sooner, it can't be caught
else cars[t-1] = cars[t]; //else, cars[t-1] arrives at same time as cars[t]
}
//t可能会为-1,ans初始值其实最好为1
return ans + (t == 0 ? 1 : 0); //lone car is fleet (if it exists)
}
}
class Car {
int position;
double time;
Car(int p, double t) {
position = p;
time = t;
}
}
作者:LeetCode
链接:https://leetcode-cn.com/problems/car-fleet/solution/che-dui-by-leetcode/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
最多能完成排序的块 II
LeetCode768,将数组切成多个子块后,排序在按顺序拼接,使得和原来的数组排序后是一致的效果。
这个问题和“最多能完成排序的块”相似,但给定数组中的元素可以重复,输入数组最大长度为2000,其中的元素最大为10**8。
arr是一个可能包含重复元素的整数数组,我们将这个数组分割成几个“块”,并将这些块分别进行排序。之后再连接起来,使得连接的结果和按升序排序后的原数组相同。
我们最多能将数组分成多少块?
示例 1:
输入: arr = [5,4,3,2,1]
输出: 1
解释:
将数组分成2块或者更多块,都无法得到所需的结果。
例如,分成 [5, 4], [3, 2, 1] 的结果是 [4, 5, 1, 2, 3],这不是有序的数组。
示例 2:
输入: arr = [2,1,3,4,4]
输出: 4
解释:
我们可以把它分成两块,例如 [2, 1], [3, 4, 4]。
然而,分成 [2, 1], [3], [4], [4] 可以得到最多的块数。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/max-chunks-to-make-sorted-ii
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
思路分析:需要记录每个子块中的最大值、当前子块的一个最大值和最小值,当遇到一个数大于当前子块的最大值时,还需判断当前子块的最小值,是否大于前面子块中的所有最大值,如果大于则可以。
int res = 1;
int[] pre = new int[arr.length];
int min = Integer.MAX_VALUE;
int max = Integer.MIN_VALUE;
int index = 0;
for (int i = 1; i < arr.length; i++) {
max = Math.max(max, arr[i - 1]);
min = Math.min(min, arr[i - 1]);
if (max <= arr[i]) {
while (index > 0 && pre[index - 1] > min) {
res--;
pre[index - 1] = Math.max(max, pre[--index]);
}
pre[index++] = max;
max = min = arr[i];
res++;
}
}
min = Math.min(min, arr[arr.length - 1]);
while (index > 0 && pre[index - 1] > min) {
res--;
index--;
}
return res;