扫描线算法模板 原理其实很容易想明白
391. 数飞机
给出飞机的起飞和降落时间的列表,用序列 interval
表示. 请计算出天上同时最多有多少架飞机?
样例
Example 1:
Input: [(1, 10), (2, 3), (5, 8), (4, 7)]
Output: 3
Explanation:
The first airplane takes off at 1 and lands at 10.
The second ariplane takes off at 2 and lands at 3.
The third ariplane takes off at 5 and lands at 8.
The forth ariplane takes off at 4 and lands at 7.
During 5 to 6, there are three airplanes in the sky.
Example 2:
Input: [(1, 2), (2, 3), (3, 4)]
Output: 1
Explanation: Landing happen before taking off.
注意事项
如果多架飞机降落和起飞在同一时刻,我们认为降落有优先权。
下面这个是参考答案:
将起飞时间和降落时间放到同一个数组中, 标识出是起飞还是降落时间, 然后对数组排序.
遍历数组即可, 碰到起飞计数器加一, 碰到降落计数器减一. 维护最大值作为答案.
注意降落优先于起飞.——当心!!!
精選裡面的Python解答使用的是cmp這個function, 然而這個cmp function在Python 3.x已經不再作為sorted(), list.sort()的參數了 下面提供的版本是依照精選Python解答改過來的,適用於Python3.x版
def sorter(x, y):
if x[0] != y[0]:
return x[0] - y[0]
return x[1] - y[1]
from functools import cmp_to_key
class Solution:
def countOfAirplanes(self, airplanes):
times = []
for airplane in airplanes:
times.append((airplane.start, 1))
times.append((airplane.end, -1))
times = sorted(times, key = cmp_to_key(sorter))
count = 0
max_count = 0
for time, delta in times:
count += delta
max_count = max(max_count, count)
return max_count
我的答案:
""" Definition of Interval. class Interval(object): def __init__(self, start, end): self.start = start self.end = end """ class Solution: """ @param airplanes: An interval array @return: Count of airplanes are in the sky. """ def countOfAirplanes(self, airplanes): # write your code here lines = [] START, END = 1, 0 for p in airplanes: lines.append((p.start, START)) lines.append((p.end, END)) lines.sort() c = 0 ans = 0 for p, flag in lines: if flag == START: c += 1 else: c -= 1 ans = max(ans, c) assert c == 0 return ans
直接排序就好了,因为python3里tuple的比较就是按照先比较第一个,再比较第二个!!!
>>> (1, 1) > (1, 2) False >>> (1, 1) < (1, 2) True >>> (2, 1) < (2, 3) True >>> (2, 1) > (2, 3) False >>> (2, 1) == (2, 1) True
L = [(‘d’,2),(‘a’,4),(‘b’,3),(‘c’,2)]
L.sort(key=lambda x:(x[1],x[0])) 先按照数字排序,再按照字母排序
L
[(‘c’, 2), (‘d’, 2), (‘b’, 3), (‘a’, 4)]
919. 会议室 II
给定一系列的会议时间间隔intervals,包括起始和结束时间[[s1,e1],[s2,e2],...] (si < ei)
,找到所需的最小的会议室数量。
样例
样例1
输入: intervals = [(0,30),(5,10),(15,20)]
输出: 2
解释:
需要两个会议室
会议室1:(0,30)
会议室2:(5,10),(15,20)
样例2
输入: intervals = [(2,7)] 输出: 1 解释: 只需要1个会议室就够了
""" Definition of Interval. class Interval(object): def __init__(self, start, end): self.start = start self.end = end """ class Solution: """ @param intervals: an array of meeting time intervals @return: the minimum number of conference rooms required """ def minMeetingRooms(self, intervals): # Write your code here lines = [] S, E = 1, 0 for i in intervals: lines.append((i.start, S)) lines.append((i.end, E)) lines.sort() ans = 0 c = 0 for t, flag in lines: if flag == S: c += 1 else: c -= 1 ans = max(ans, c) assert c == 0 return ans
输出两组区间的交集
描述
目前有两个用户的有序在线时间序列,每一个区间记录了该用户的登录时间点x
和离线时间点y
,请找出这两个用户同时在线的时间段,输出的时间段请从小到大排序。
你需要返回一个intervals的列表
样例
样例 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)]
输出:[]
解释:不存在任何时间段,两个用户同时在线。
30行超简单扫描线算法,仍然是遇到start,count++,遇到end,count--,那么intersection就是找出所有时刻(x, x+1),其中x时刻count=2,x+1时刻的count=1
public class Solution {
public List<Interval> timeIntersection(List<Interval> seqA, List<Interval> seqB) {
seqA.addAll(seqB);
List<Point> points = new ArrayList<>();
for (Interval interval : seqA) {
points.add(new Point(interval.start, true));
points.add(new Point(interval.end, false));
}
Collections.sort(points, (p1, p2) -> p1.x - p2.x);
int count = 0;
int start = -1;
List<Interval> res = new ArrayList<>();
for (Point p : points) {
if (p.ifStart) count++;
else count--;
if (count == 2) start = p.x;
if (count == 1 && start != -1) {
res.add(new Interval(start, p.x));
start = -1;
}
}
return res;
}
class Point {
int x;
boolean ifStart;
public Point(int x, boolean ifStart) {
this.x = x;
this.ifStart = ifStart;
}
}
}
当然,还有双指针解法 ==>以前在没有学sweep line的时候我就是这样做的。。。
可以用二路归并相似的思路,用两个指针来分别记录两个list的当前扫描位置
然后分情况讨论
-
如果两个区间没有交集, seqAi.start > seqBj.end or seqAi.end < seqBj.start 在这种情况下就将end小的List的指针向前进一
-
如果两个区间有交集, 则统计 start 为两个start中大的那个, end 为两个end 中小的那个, 将这个区间放入答案集合, 并且前进end小的那个list的指针
因为两个list都是按start sorted并且没有Overlap, 所以结果也一定是按相同顺序排序 时间复杂度O(N) N为两个List中相对较短的长度, 空间复杂度 O(1)
class Solution:
"""
@param seqA: the list of intervals
@param seqB: the list of intervals
@return: the time periods
"""
def timeIntersection(self, seqA, seqB):
# Write your code here
if seqA == None or seqB == None or len(seqA) == None or len(seqB) == None:
return []
i,j = 0, 0
ans = []
while i < len(seqA) and j < len(seqB):
if seqA[i].start > seqB[j].end:
j += 1
continue
elif seqB[j].start > seqA[i].end:
i += 1
continue
if seqA[i].start <= seqB[j].start:
start = seqB[j].start
elif seqA[i].start > seqB[j].start:
start = seqA[i].start
if seqA[i].end <= seqB[j].end:
end = seqA[i].end
i += 1
elif seqA[i].end > seqB[j].end:
end = seqB[j].end
j += 1
ans.append(Interval(start, end))
return ans