适用情况
当你需要产生一堆相互之间没有交集的区间的时候 当你听到重叠区间的时候
模式概括
intervals.sort(<1.排序>)
for <进入循环>:
if a1,a2,b1,b2..<2.交集条件>
max(),min()<3.找出区间>
res.append()<加入res列表>
解决思路:
- 把每个区间按start排序,区间起始点为最小的start
- 循环判断两个区间是否重叠(对于很多元素的对比,化简的思路是先只看两个元素怎么比较,然后循环迭代)
- 重叠则找max end;不重叠则加入一个新区间元素
例一 合并区间(medium)
leetcode 56: 给出一个区间的集合,请合并所有重叠的区间。
输入: [[1,3],[2,6],[8,10],[15,18]] 输出: [[1,6],[8,10],[15,18]] 解释: 区间 [1,3] 和 [2,6] 重叠, 将它们合并为 [1,6].
输入: [[1,4],[4,5]] 输出: [[1,5]] 解释: 区间 [1,4] 和 [4,5] 可被视为重叠区间。
解题思路 1、以终为始:结果是一个新的列表,嵌套互不重叠的区间,所以一开始创建一个res = [],把符合条件的区间append到这里。 2、审题目要求:“合并重叠的区间”,其实就是对比区间的起始,对于很多元素的对比,化简的思路是先只看两个元素怎么比较,然后循环迭代,要两个重叠的区间合并起来,两者的最小值是start,最大值是end,两个两个对比。 问题1:如果出现不重叠怎么办?就分情况,刚刚讨论的是重叠的情况(if),不重叠的话就append到列表(else) 问题2:怎么选择对比的区间?列表里那么多区间,按什么顺序去对比?按start的大小顺序排,所以要先按顺序排列起来,再进行循环对比,每次对比的时候取res列表中最后一个区间跟当前循环到的区间对比
def merge(intervals):
if len(intervals) < 2:
return intervals
# 以终为始
res = []
# 1 按区间的 start 升序排列
intervals.sort(key = lambda x: x[0])
res.append(intervals[0])
# 2 循环对比去除重叠区间
for i in range(1, len(intervals)):
a = res[-1]
b = intervals[i]
if a[1] >= b[0]:
# a[1] = max(a[1], b[1])
# 找到最大的 end 用下面这种可以减少一半的运行
if a[1] < b[1]:
a[1] = b[1]
else:
res.append(b)
return res
intervals.sort(key = lambda x: x[0])
语法笔记:当待排序列表的元素由多字段构成时(如嵌套列表、元组),可以通过sorted(iterable,key,reverse)的参数key来制定我们根据哪个字段对列表元素进行排序。如对[[6,7], [2,4], [5,9]],指定x.start就是指定区间内的起始值进行排序。(这个x是随便哪个取的,代表intervals中的元素,只要后面表达式对应即可)
例二 区间列表的交集(medium)
leetcode 986 给定两个由一些闭区间组成的列表,每个区间列表都是成对不相交的,并且已经排序。
返回这两个区间列表的交集。
(形式上,闭区间 [a, b](其中 a <= b)表示实数 x 的集合,而 a <= x <= b。两个闭区间的交集是一组实数,要么为空集,要么为闭区间。例如,[1, 3] 和 [2, 4] 的交集为 [2, 3]。)
输入:A = [[0,2],[5,10],[13,23],[24,25]], B = [[1,5],[8,12],[15,24],[25,26]] 输出:[[1,2],[5,5],[8,10],[15,23],[24,24],[25,25]] 注意:输入和所需的输出都是区间对象组成的列表,而不是数组或列表。
提示:
0 <= A.length < 1000 0 <= B.length < 1000 0 <= A[i].start, A[i].end, B[i].start, B[i].end < 10^9
解题思路 1、以终为始,创建res=[] 2、排序 3、进入循环,通过双指针游走找交集 问题一:怎么设定两个指针怎么移动?
画图找规律,指针是否前进取决于a2和b2的关系 if b2 < a2: j += 1 else: i += 1问题二:怎么判断交集?正向寻找情况比较多时,尝试逆向思维,看什么时候没有交集:
if b2 < a1 or a2 < b1: [a1,a2] 和 [b1,b2] 无交集 ```
所以有交集的情况就是不等号取反,or 也要变成 and
if b2 >= a1 and a2 >= b1: [a1,a2] 和 [b1,b2] 存在交集问题三:怎么找交集区间?举出所有情况,找规律
结合上一题合并区间用max的启发,这里的交集就是 start = max(a1, b1) end = min(a2, b2)
def intervalIntersection(A, B):
i, j = 0, 0 # 双指针循环前要赋值
res = []
# <进入循环>
while i < len(A) and j < len(B):
a1, a2 = A[i][0], A[i][1]
b1, b2 = B[j][0], B[j][1]
c = []
# <判断交集>
if b1 <= a2 and a1 <= b2:
# <找出区间>
c.append(max(a1, b1))
c.append(min(a2, b2))
res.append(c)
# <循环条件>
if b2 < a2:
j += 1
else:
i += 1
return res
时间复杂度O(n)
参考资料:
Grokking the Coding Interview: Patterns for Coding Questions