LeetCode 986
方法1:扫描线(不推荐,我就写着玩)
时间复杂度:O((m+n)log(m+n)), m = firstList.length, n = secondList.length 想法:我之前写区间的题写魔怔了,上去看了一眼数据规模,然后直接扫描线搞了,不过果然beat率非常低,我这里就是写写复习一下扫描线。就像天上有多少架飞机那道题,这里什么时候有俩正在进行的了就记一下起始时间,什么时候又变成只有一个会在开了就放进结果数组。 代码:
class Event {
public int time, start;
public Event(int time, int start) {
this.time = time;
this.start = start;
}
}
class EventComparator implements Comparator<Event> {
public int compare(Event e1, Event e2) {
if (e1.time != e2.time) {
return e1.time - e2.time;
}
return e1.start - e2.start;
}
}
class Solution {
public int[][] intervalIntersection(int[][] firstList, int[][] secondList) {
List<Event> events = new ArrayList<>();
for (int[] f : firstList) {
events.add(new Event(f[0], 0));
events.add(new Event(f[1], 1));
}
for (int[] s : secondList) {
events.add(new Event(s[0], 0));
events.add(new Event(s[1], 1));
}
Collections.sort(events, new EventComparator());
int cnt = 0;
int l = 0, r = 0;
List<int[]> lst = new ArrayList<>();
for (Event e : events) {
if (e.start == 0) {
cnt++;
if (cnt == 2) {
l = e.time;
}
}
else {
cnt--;
if (cnt == 1) {
r = e.time;
lst.add(new int[]{l, r});
}
}
}
int[][] res = new int[lst.size()][2];
for (int i = 0; i < lst.size(); i++) {
res[i] = lst.get(i);
}
return res;
}
}
方法2:双指针
时间复杂度:O(m + n) 想法:给第一个数组一个指针i,给第二个数组一个指针j,直接开扫。如果目前i指向的这个整个已经在j指向的那个前面了,那就得把i后移,相似反之情况把j后移。如果这俩指的有交集,那就先记一下交集,然后让目前指向的那个元素结束比较早的指针往后移。因为这个时候它后移,另一个指针指向的那个结束比较晚,这俩还有可能再有交集,否则就完全没可能。如果结束时间相同,俩都后移。比较直观的做法。 代码:
class Solution {
public int[][] intervalIntersection(int[][] firstList, int[][] secondList) {
List<int[]> lst = new ArrayList<>();
int m = firstList.length, n = secondList.length, i = 0, j = 0;
while (i < m && j < n) {
if (firstList[i][1] < secondList[j][0]) {
i++;
}
else if (firstList[i][0] > secondList[j][1]) {
j++;
}
else {
lst.add(new int[] {Math.max(firstList[i][0], secondList[j][0]), Math.min(firstList[i][1], secondList[j][1])});
if (firstList[i][1] < secondList[j][1]) {
i++;
}
else if (firstList[i][1] > secondList[j][1]) {
j++;
}
else {
i++;
j++;
}
}
}
int[][] res = new int[lst.size()][2];
for (int k = 0; k < lst.size(); k++) {
res[k] = lst.get(k);
}
return res;
}
}
突然发现简书一天之内最多发两篇文章,那以后合并着发
LeetCode 1353
方法:优先队列
时间复杂度:O(nlogn) 想法:这个题其实是来自计算机操作系统的一个结论,是CPU调度那一节。这种问题要是想做的尽可能多的话,就是earliest deadline first。实际上我们可以维护一个cur变量,代表我现在考虑的时刻是什么时候,然后每次循环放进去一些现在可以开始参加了的event(那么这里会看到得给events排序),然后用堆PriorityQueue来动态拿结束时间最早的任务。我们for循环,如果说堆不为空的话,每次cur加一,因为堆不为空,那么cur这个时间就可以参加一个event,那么cur+1就可以在下一次循环考虑下一个时刻。但如果堆为空了,直接就置成events[i][0]。然后每次for循环最后把一堆已经不可能再参加的event扔掉。比方说有好几个[3,5]event,在5这个时刻选择了参加其中一个,那么这时候下一次循环需要考虑的时刻就是6,其余的event就永远不可能参加了,那因为我们每次取出结束时间最早的元素,这个放在里面会搞乱整个程序,因此每次for循环结束把这种元素从堆里删掉。 代码:
class Solution {
public int maxEvents(int[][] events) {
Arrays.sort(events, (a, b) -> a[0] - b[0]);
Queue<int[]> heap = new PriorityQueue<>((a, b) -> a[1] - b[1]);
int res = 0, cur = 0, i = 0, n = events.length;
while (i < n || !heap.isEmpty()) {
if (heap.isEmpty()) {
cur = events[i][0];
}
while (i < n && events[i][0] == cur) {
heap.offer(events[i++]);
}
res++;
heap.poll();
cur++;
while (!heap.isEmpty() && heap.peek()[1] < cur) {
heap.poll();
}
}
return res;
}
}