排序题目:插入区间
文章目录
- 题目
- 标题和出处
- 难度
- 题目描述
- 要求
- 示例
- 数据范围
- 解法一
- 思路和算法
- 代码
- 复杂度分析
- 解法二
- 思路和算法
- 代码
- 复杂度分析
题目
标题和出处
标题:插入区间
出处:57. 插入区间
难度
5 级
题目描述
要求
给定无重叠的区间数组 intervals \texttt{intervals} intervals,其中 intervals[i] = [start i , end i ] \texttt{intervals[i] = [start}_\texttt{i}\texttt{, end}_\texttt{i}\texttt{]} intervals[i] = [starti, endi],表示第 i \texttt{i} i 个区间的开始位置和结束位置, intervals \texttt{intervals} intervals 按照 start i \texttt{start}_\texttt{i} starti 升序排序。同时给定一个区间 newInterval = [start, end] \texttt{newInterval = [start, end]} newInterval = [start, end],表示另一个区间的开始位置和结束位置。
将 newInterval \texttt{newInterval} newInterval 插入到 intervals \texttt{intervals} intervals 中,使得 intervals \texttt{intervals} intervals 仍按照 start i \texttt{start}_\texttt{i} starti 升序排序且 intervals \texttt{intervals} intervals 仍没有重叠的区间(必要的情况下合并重叠的区间)。
返回插入区间后的 intervals \texttt{intervals} intervals。
示例
示例 1:
输入:
intervals
=
[[1,3],[6,9]],
newInterval
=
[2,5]
\texttt{intervals = [[1,3],[6,9]], newInterval = [2,5]}
intervals = [[1,3],[6,9]], newInterval = [2,5]
输出:
[[1,5],[6,9]]
\texttt{[[1,5],[6,9]]}
[[1,5],[6,9]]
示例 2:
输入:
intervals
=
[[1,2],[3,5],[6,7],[8,10],[12,16]],
newInterval
=
[4,8]
\texttt{intervals = [[1,2],[3,5],[6,7],[8,10],[12,16]], newInterval = [4,8]}
intervals = [[1,2],[3,5],[6,7],[8,10],[12,16]], newInterval = [4,8]
输出:
[[1,2],[3,10],[12,16]]
\texttt{[[1,2],[3,10],[12,16]]}
[[1,2],[3,10],[12,16]]
解释:因为新的区间
[4,8]
\texttt{[4,8]}
[4,8] 与
[3,5],[6,7],[8,10]
\texttt{[3,5],[6,7],[8,10]}
[3,5],[6,7],[8,10] 重叠。
数据范围
- 1 ≤ intervals.length ≤ 10 4 \texttt{1} \le \texttt{intervals.length} \le \texttt{10}^\texttt{4} 1≤intervals.length≤104
- intervals[i].length = 2 \texttt{intervals[i].length} = \texttt{2} intervals[i].length=2
- 0 ≤ start i ≤ end i ≤ 10 5 \texttt{0} \le \texttt{start}_\texttt{i} \le \texttt{end}_\texttt{i} \le \texttt{10}^\texttt{5} 0≤starti≤endi≤105
- intervals \texttt{intervals} intervals 根据 start i \texttt{start}_\texttt{i} starti 按升序排序
- newInterval.length = 2 \texttt{newInterval.length} = \texttt{2} newInterval.length=2
- 0 ≤ start ≤ end ≤ 10 5 \texttt{0} \le \texttt{start} \le \texttt{end} \le \texttt{10}^\texttt{5} 0≤start≤end≤105
解法一
思路和算法
这道题是「合并区间」的延伸,要求在已经有序的无重叠区间数组中插入一个新区间,插入之后合并重叠的区间,使得插入区间后的数组仍然满足有序且无重叠区间。
由于插入区间的操作等价于在数组中增加一个区间然后合并重叠区间,并保持区间有序,因此可以使用「合并区间」的做法。使用一个列表存储数组 intervals \textit{intervals} intervals 中的所有区间和新区间,将列表中的所有重叠区间合并。
首先将列表中的所有区间(包括新区间)按照开始位置升序排序,如果存在多个区间的开始位置相同则按照结束位置降序排序,然后遍历排序后的列表,合并重叠的区间。由于列表中的区间按照开始位置升序排序,因此合并重叠区间之后,列表中的区间符合按照开始位置升序排序的规则。
遍历结束之后,将列表转成数组返回。
代码
class Solution {
public int[][] insert(int[][] intervals, int[] newInterval) {
List<int[]> intervalList = new ArrayList<int[]>();
for (int[] interval : intervals) {
intervalList.add(interval);
}
intervalList.add(newInterval);
Collections.sort(intervalList, (a, b) -> {
if (a[0] != b[0]) {
return a[0] - b[0];
} else {
return b[1] - a[1];
}
});
List<int[]> mergedList = new ArrayList<int[]>();
mergedList.add(intervalList.get(0));
int size = intervalList.size();
for (int i = 1; i < size; i++) {
int[] curr = intervalList.get(i);
int[] prev = mergedList.get(mergedList.size() - 1);
if (curr[0] == prev[0]) {
continue;
}
if (curr[0] <= prev[1]) {
prev[1] = Math.max(prev[1], curr[1]);
} else {
mergedList.add(curr);
}
}
return mergedList.toArray(new int[mergedList.size()][]);
}
}
复杂度分析
-
时间复杂度: O ( n log n ) O(n \log n) O(nlogn),其中 n n n 是数组 intervals \textit{intervals} intervals 的长度。排序需要 O ( n log n ) O(n \log n) O(nlogn) 的时间,排序后遍历列表合并区间需要 O ( n ) O(n) O(n) 的时间,时间复杂度是 O ( n log n ) O(n \log n) O(nlogn)。
-
空间复杂度: O ( n ) O(n) O(n),其中 n n n 是数组 intervals \textit{intervals} intervals 的长度。列表需要 O ( n ) O(n) O(n) 的空间。
解法二
思路和算法
由于数组 intervals \textit{intervals} intervals 已经有序,因此可以直接遍历数组 intervals \textit{intervals} intervals 完成插入 newInterval \textit{newInterval} newInterval 的操作。
遍历数组 intervals \textit{intervals} intervals 的过程中,使用列表存储插入操作后的结果区间,应确保遍历所有区间(包括已有的区间和新区间)的顺序是按照开始位置升序的顺序,依次将每个遍历到的区间添加到列表。用 curr \textit{curr} curr 表示当前遍历到的区间,如果 newInterval \textit{newInterval} newInterval 尚未插入且 curr [ 0 ] ≥ newInterval [ 0 ] \textit{curr}[0] \ge \textit{newInterval}[0] curr[0]≥newInterval[0],则在将 curr \textit{curr} curr 添加到列表之前,首先将 newInterval \textit{newInterval} newInterval 添加到列表。
每次将区间添加到列表的同时,判断新添加的区间是否和列表中已有的区间存在重叠,如果存在重叠则执行合并操作。由于遍历区间的顺序是按照开始位置升序的顺序,且列表中已有的区间互不重叠,因此当列表不为空时,只需要将新添加的区间和列表中的最后一个区间比较即可。具体操作如下。
-
如果列表不为空且当前区间的开始位置小于等于列表中的最后一个区间的结束位置,则将当前区间与列表中的最后一个区间合并,将列表中的最后一个区间的结束位置更新为两个区间的结束位置中的最大值。
-
如果列表为空或当前区间的开始位置大于列表中的最后一个区间的结束位置,则将当前区间添加到列表中。
遍历结束之后,如果 newInterval \textit{newInterval} newInterval 仍未插入,则将 newInterval \textit{newInterval} newInterval 添加到列表。
最后将列表转成数组返回。
代码
class Solution {
public int[][] insert(int[][] intervals, int[] newInterval) {
List<int[]> intervalList = new ArrayList<int[]>();
boolean inserted = false;
int length = intervals.length;
for (int i = 0; i < length; i++) {
int[] curr = intervals[i];
if (!inserted && curr[0] >= newInterval[0]) {
insertInterval(intervalList, newInterval);
inserted = true;
}
insertInterval(intervalList, curr);
}
if (!inserted) {
insertInterval(intervalList, newInterval);
}
return intervalList.toArray(new int[intervalList.size()][]);
}
public void insertInterval(List<int[]> intervalList, int[] interval) {
boolean merged = false;
if (!intervalList.isEmpty()) {
int[] prev = intervalList.get(intervalList.size() - 1);
if (interval[0] <= prev[1]) {
prev[1] = Math.max(prev[1], interval[1]);
merged = true;
}
}
if (!merged) {
intervalList.add(interval);
}
}
}
复杂度分析
-
时间复杂度: O ( n ) O(n) O(n),其中 n n n 是数组 intervals \textit{intervals} intervals 的长度。需要遍历数组 intervals \textit{intervals} intervals 一次完成区间的插入,对于每个区间的操作时间都是 O ( 1 ) O(1) O(1)。
-
空间复杂度: O ( n ) O(n) O(n),其中 n n n 是数组 intervals \textit{intervals} intervals 的长度。存储合并后的区间的列表需要 O ( n ) O(n) O(n) 的空间。