区间问题
区间问题就是线段问题,让你合并所有线段,找出线段的交集等。主要有两个技巧:
1,排序: 常见的排序方法就是按照区间起点排序,或者先按照起点升序排序,若起点相同,则按照终点降序排列。
2,画图: 画图可以更直观的看出两个区间的相对位置到底有几种可能,不同的相对位置,我们应该如何去解决。
1288. 删除被覆盖区间
给你一个区间列表,请你删除列表中被其他区间所覆盖的区间。
只有当 c <= a 且 b <= d 时,我们才认为区间 [a,b) 被区间 [c,d) 覆盖。
在完成所有删除操作后,请你返回列表中剩余区间的数目。
示例:
输入:intervals = [[1,4],[3,6],[2,8]]
输出:2
解释:区间 [3,6] 被区间 [2,8] 覆盖,所以它被删除了。
思路:
我们可以先算出被覆盖的区间有几个,最后再用总数减去即可。
第一步,我们先排序:
排序之后,两个相邻区间可能有如下三种相对位置:
对于这三种情况,我们应该这样处理:
对于情况一,找到了覆盖区间。
对于情况二,两个区间可以合并,成一个大区间。
对于情况三,两个区间完全不相交。
所以代码如下:
1 | class Solution { |
56. 合并区间
以数组 intervals 表示若干个区间的集合,其中单个区间为 intervals[i] = [starti, endi] 。请你合并所有重叠的区间,并返回 一个不重叠的区间数组,该数组需恰好覆盖输入中的所有区间 。
示例:
输入:intervals = [[1,3],[2,6],[8,10],[15,18]]
输出:[[1,6],[8,10],[15,18]]
解释:区间 [1,3] 和 [2,6] 重叠, 将它们合并为 [1,6].
思路:
我们解决区间问题一般是先排序,然后画图。我们按照起点升序排序,得到以下图形。
显然,对于相交的线段,最小的起点就是start
最小的,end
为其中结尾最大的。那问题就转化为找到相交中起点最小的和终点最大的,
由于我们已经按照起点排序了,那么第一位就是我们的start
,那么end
应该如何确定呢,我们只需要判断下一个要来的起点是否小于当前的终点,如果小于,那么终点就是这两个结尾的最大值。
例如[1,3]和[2,6],2 < 3 所以必相交,终点就更新为max(3, 6) = 6;
代码如下:
1 | class Solution { |
986. 区间列表的交集
给定两个由一些 闭区间 组成的列表,firstList 和 secondList ,其中 firstList[i] = [starti, endi] 而 secondList[j] = [startj, endj] 。每个区间列表都是成对 不相交 的,并且 已经排序 。
返回这 两个区间列表的交集 。
形式上,闭区间 [a, b](其中 a <= b)表示实数 x 的集合,而 a <= x <= b 。
两个闭区间的 交集 是一组实数,要么为空集,要么为闭区间。例如,[1, 3] 和 [2, 4] 的交集为 [2, 3] 。
示例:
输入:firstList = [[0,2],[5,10],[13,23],[24,25]], secondList = [[1,5],[8,12],[15,24],[25,26]]
输出:[[1,2],[5,5],[8,10],[15,23],[24,24],[25,25]]
思路:
第一步,排序,已经排过序了,那我们使用双指针。
我们分析下各种情况:
- 对于两个区间,我们用
[a1,a2]
和[b1,b2]
表示在A 和 B中的两个区间,那么什么时候没有交集呢?
只有这两种情况:
1 | if(b2 < a1 || a2 < b1){ |
那么什么时候存在交集呢?根据命题的否定
1 | if(b2 >= a1 && a2 >= b1){ |
穷举出来如下:
这很简单吧,就这四种情况而已。那么接下来思考,这几种情况下,交集是否有什么共同点呢?
我们惊奇地发现,交集区间是有规律的!如果交集区间是[c1,c2]
,那么c1=max(a1,b1)
,c2=min(a2,b2)
!这一点就是寻找交集的核心,我们把代码更进一步:
1 | while i < len(A) and j < len(B): |
最后一步,我们的指针 i
和j
什么时候前进?
我们发现当 b2 <
a2时,我们的j
会往前,因为此时这个线段的最长已经在A的里面了。
同理当 b2 >= a2 时, i++
;
所以,完整代码如下:
1 | class Solution { |