Number of Airplanes in the Sky
给出飞机的起飞和降落时间的列表,用序列 interval
表示. 请计算出天上同时最多有多少架飞机?
Example
样例 1:
输入: [(1, 10), (2, 3), (5, 8), (4, 7)]
输出: 3
解释:
第一架飞机在1时刻起飞, 10时刻降落.
第二架飞机在2时刻起飞, 3时刻降落.
第三架飞机在5时刻起飞, 8时刻降落.
第四架飞机在4时刻起飞, 7时刻降落.
在5时刻到6时刻之间, 天空中有三架飞机.
思路:经典扫描线算法:把interval起飞和降落做为event,全部打散,按照时间排列,同时时间相等的,按照降落在前面,起飞在后面进行排序;最后再扫一遍,遇见start,count++,遇见end,count--,然后最后中间出现的最大值,就是题目所求。
/**
* Definition of Interval:
* public classs Interval {
* int start, end;
* Interval(int start, int end) {
* this.start = start;
* this.end = end;
* }
* }
*/
public class Solution {
/**
* @param airplanes: An interval array
* @return: Count of airplanes are in the sky.
*/
private class Node {
int time;
int flag; // 1 take off, 0 is landing;
public Node(int time, int flag) {
this.time = time;
this.flag = flag;
}
}
private class NodeComparator implements Comparator<Node> {
@Override
public int compare(Node a, Node b) {
if(a.time != b.time) {
return a.time - b.time;
} else {
// 定义1是起飞,0是下降,这样如果按照大小排序,那么就是如果时间相等的情况下:下降的在前面,起飞的在后面。
return a.flag - b.flag;
}
}
}
public int countOfAirplanes(List<Interval> airplanes) {
if(airplanes == null || airplanes.size() == 0) {
return 0;
}
int n = airplanes.size();
Node[] nodes = new Node[n * 2];
int index = 0;
for(Interval interval : airplanes) {
nodes[index++] = new Node(interval.start, 1);
nodes[index++] = new Node(interval.end, 0);
}
Arrays.sort(nodes, new NodeComparator());
int count = 0;
int maxcount = 0;
for(int i = 0; i < nodes.length; i++) {
if(nodes[i].flag == 1) {
count++;
}
if(nodes[i].flag == 0) {
count--;
}
maxcount = Math.max(count, maxcount);
}
return maxcount;
}
}
Meeting Rooms II
给定一系列的会议时间间隔intervals,包括起始和结束时间[[s1,e1],[s2,e2],...] (si < ei)
,找到所需的最小的会议室数量。
Example
样例1
输入: intervals = [(0,30),(5,10),(15,20)]
输出: 2
解释:
需要两个会议室
会议室1:(0,30)
会议室2:(5,10),(15,20)
思路:题目跟上面飞机一模一样,代码都是一样的。
/**
* Definition of Interval:
* public classs Interval {
* int start, end;
* Interval(int start, int end) {
* this.start = start;
* this.end = end;
* }
* }
*/
public class Solution {
/**
* @param intervals: an array of meeting time intervals
* @return: the minimum number of conference rooms required
*/
private class Meeting {
public int time;
public int flag;
public Meeting (int time, int flag) {
this.time = time;
this.flag = flag; // define 1 as start, 0 as end;
}
}
private class MeetingComparator implements Comparator<Meeting> {
@Override
public int compare(Meeting a, Meeting b) {
if(a.time != b.time) {
return a.time - b.time;
} else {
return a.flag - b.flag;
}
}
}
public int minMeetingRooms(List<Interval> intervals) {
if(intervals == null || intervals.size() == 0) {
return 0;
}
int n = intervals.size();
Meeting[] meetings = new Meeting[n * 2];
int index = 0;
for(Interval interval : intervals) {
meetings[index++] = new Meeting(interval.start, 1);
meetings[index++] = new Meeting(interval.end, 0);
}
Arrays.sort(meetings, new MeetingComparator());
int count = 0;
int maxcount = 0;
for(int i = 0; i < meetings.length; i++) {
if(meetings[i].flag == 1) {
count++;
}
if(meetings[i].flag == 0) {
count--;
}
maxcount = Math.max(maxcount, count);
}
return maxcount;
}
}
Time Intersection
目前有两个用户的有序在线时间序列,每一个区间记录了该用户的登录时间点x
和离线时间点y
,请找出这两个用户同时在线的时间段,输出的时间段请从小到大排序。你需要返回一个intervals的列表
Example
样例 1:
输入:seqA = [(1,2),(5,100)], seqB = [(1,6)]
输出:[(1,2),(5,6)]
解释:在 (1,2), (5,6) 这两个时间段内,两个用户同时在线。
样例 2:
输入:seqA = [(1,2),(10,15)], seqB = [(3,5),(7,9)]
输出:[]
解释:不存在任何时间段,两个用户同时在线。
Notice
- 我们保证在线时间序列的长度
1 <= len <= 1e6
。 - 我们保证在线时间序列是合法的,即对于某一个用户的在线时间序列,它的任意两个区间不相交。
思路:扫描线算法,注意的是,阈值发生变化的时候,收集数据,一定要在count = 2的时候收集。
/**
* Definition of Interval:
* public classs Interval {
* int start, end;
* Interval(int start, int end) {
* this.start = start;
* this.end = end;
* }
* }
*/
public class Solution {
/**
* @param seqA: the list of intervals
* @param seqB: the list of intervals
* @return: the time periods
*/
private class Node {
public int time;
public int flag;
public Node(int time, int flag) {
this.time = time;
this.flag = flag;
}
}
private class NodeComparator implements Comparator<Node> {
@Override
public int compare(Node a, Node b) {
if(a.time != b.time) {
return a.time - b.time;
} else {
return a.flag - b.flag; // define flag 1 is online, 0 is offline;
}
}
}
public List<Interval> timeIntersection(List<Interval> seqA, List<Interval> seqB) {
List<Interval> result = new ArrayList<Interval>();
if(seqA == null || seqA.size() == 0 || seqB == null || seqB.size() == 0) {
return result;
}
int n = seqA.size();
int m = seqB.size();
Node[] nodes = new Node[n * 2 + m * 2];
int index = 0;
for(int i = 0; i < n; i++) {
nodes[index++] = new Node(seqA.get(i).start, 1);
nodes[index++] = new Node(seqA.get(i).end, 0);
}
for(int i = 0; i < m; i++) {
nodes[index++] = new Node(seqB.get(i).start, 1);
nodes[index++] = new Node(seqB.get(i).end, 0);
}
Arrays.sort(nodes, new NodeComparator());
int count = 0;
int start = 0;
int end = 0;
for(int i = 0; i < nodes.length; i++) {
if(nodes[i].flag == 1) {
count++;
if(count == 2) {
start = nodes[i].time;
}
}
if(nodes[i].flag == 0) {
if(count == 2) { //每次都是阈值发生变化的时候,收集interval;
end = nodes[i].time;
result.add(new Interval(start, end));
start = 0;
end = 0;
}
count--;
}
}
return result;
}
}
思路2:这题因为数据是sorted,因为A和B都是sorted,那么跟merge array一样,可以用打擂台的方式,每次踢走一个。
如果Max(startA, startB) < Min(endA, endB)则加入相交的interval 看A, B end 谁大,谁留下,另外一个踢走;O(N+M)
// 因为数据是sorted,那么就按照overlap来扫描,也就是start取最大值,end取最小值,
// 如果start< end,表明有overlap,否则不会有overlap,同时end较小的扔掉,较大的留下;
/**
* Definition of Interval:
* public classs Interval {
* int start, end;
* Interval(int start, int end) {
* this.start = start;
* this.end = end;
* }
* }
*/
public class Solution {
/**
* @param seqA: the list of intervals
* @param seqB: the list of intervals
* @return: the time periods
*/
public List<Interval> timeIntersection(List<Interval> seqA, List<Interval> seqB) {
List<Interval> result = new ArrayList<Interval>();
if(seqA == null || seqA.size() == 0 || seqB == null || seqB.size() == 0) {
return result;
}
int aIndex = 0;
int bIndex = 0;
// 因为数据是sorted,那么就按照overlap来扫描,也就是start取最大值,end取最小值,
// 如果start< end,表明有overlap,否则不会有overlap,同时end较小的扔掉,较大的留下;
while(aIndex < seqA.size() && bIndex < seqB.size()) {
int start = Math.max(seqA.get(aIndex).start, seqB.get(bIndex).start);
int end = Math.min(seqA.get(aIndex).end, seqB.get(bIndex).end);
if(start < end) {
result.add(new Interval(start, end));
}
if(seqA.get(aIndex).end < seqB.get(bIndex).end) {
aIndex++;
} else {
bIndex++;
}
}
return result;
}
}
水平面上有 N 座大楼,每座大楼都是矩阵的形状,可以用一个三元组表示 (start, end, height)
,分别代表其在x轴上的起点,终点和高度。大楼之间从远处看可能会重叠,求出 N 座大楼的外轮廓线。
外轮廓线的表示方法为若干三元组,每个三元组包含三个数字 (start, end, height),代表这段轮廓的起始位置,终止位置和高度。
Example
样例1
输入:
[
[1, 3, 3],
[2, 4, 4],
[5, 6, 1]
]
输出:
[
[1, 2, 3],
[2, 4, 4],
[5, 6, 1]
]
思路:扫描线算法;注意:排序的时候,时间排序,然后起飞排在降落前面,然后高度倒序排,要让大的在前面,后面如果x相同的话,只计算大的,小的不用计算。另外,pq可能为空,为空就按照0来计算;
public class Solution {
/**
* @param buildings: A list of lists of integers
* @return: Find the outline of those buildings
*/
private class Node {
private int x;
private int height;
private int flag;
public Node (int x, int height, int flag) {
this.x = x;
this.height = height;
this.flag = flag; // define flag = 1, start, flag = 0, end;
}
}
private class NodeComparator implements Comparator<Node> {
@Override
public int compare(Node a, Node b) {
if(a.x != b.x) {
return a.x - b.x;
} else {
if(a.flag != b.flag) {
return b.flag - a.flag; // 让起飞在前面;
} else {
return b.height - a.height;
// 让高度最大的排前面;这样,同样x的,只考虑最高的;
}
}
}
}
public List<List<Integer>> buildingOutline(int[][] buildings) {
List<List<Integer>> lists = new ArrayList<List<Integer>>();
if(buildings == null || buildings.length == 0 || buildings[0].length == 0) {
return lists;
}
int n = buildings.length;
Node[] nodes = new Node[n * 2];
PriorityQueue<Integer> pq = new PriorityQueue<Integer>(n * 2, Collections.reverseOrder());
int index = 0;
for(int i = 0; i < n; i++) {
int start = buildings[i][0];
int end = buildings[i][1];
int height = buildings[i][2];
nodes[index++] = new Node(start, height, 1);
nodes[index++] = new Node(end, height, 0);
}
Arrays.sort(nodes, new NodeComparator());
int preheight = 0;
List<Node> candidates = new ArrayList<Node>();
for(int i = 0; i < nodes.length; i++) {
if(nodes[i].flag == 1) {
pq.add(nodes[i].height);
}
if(nodes[i].flag == 0) {
pq.remove(nodes[i].height);
}
int curheight = pq.isEmpty() ? 0 : pq.peek(); // 判断pq是否为空;
if(preheight != curheight) {
candidates.add(new Node(nodes[i].x, curheight, -1));
preheight = curheight;
}
}
convertCandiate(candidates, lists);
return lists;
}
private void convertCandiate(List<Node> candidates, List<List<Integer>> lists) {
List<Integer> list = new ArrayList<Integer>();
for(int i = 0; i < candidates.size() -1; i++) {
Node first = candidates.get(i);
Node second = candidates.get(i+1);
if(first.height == 0 || first.x == second.x) {
continue;
} else {
list.add(first.x);
list.add(second.x);
list.add(first.height);
lists.add(new ArrayList<Integer>(list));
list = new ArrayList<Integer>();
}
}
}
}