题目:给出一个无重叠的 ,按照区间起始端点排序的区间列表。
在列表中插入一个新的区间,你需要确保列表中的区间仍然有序且不重叠(如果有必要的话,可以合并区间)
示例:
题目解析:
方法一:贪心算法
贪心算法:
贪心算法一般用来解决需要 “找到要做某事的最小数量” 或 “找到在某些情况下适合的最大物品数量” 的问题,且提供的是无序的输入。
贪心算法的思想是每一步都选择最佳解决方案,最终获得全局最佳的解决方案。
标准解决方案具有 O(NlogN) 的时间复杂度且由以下两部分组成:
思考如何排序输入数据O(NlogN) 的时间复杂度)。
思考如何解析排序后的数据O(N) 的时间复杂度)
如果输入数据本身有序,则我们不需要进行排序,那么该贪心算法具有 O(N) 的时间复杂度。
如何证明你的贪心思想具有全局最优的效果:可以使用反证法来证明。
算法:
将 newInterval 之前开始的区间添加到输出。
添加 newInterval 到输出,若 newInterval 与输出中的最后一个区间重合则合并他们。
一个个添加区间到输出,若有重叠部分则合并他们
class Solution {
public int[][] insert(int[][] intervals, int[] newInterval) {
// init data
int newStart = newInterval[0], newEnd = newInterval[1];
int idx = 0, n = intervals.length;
LinkedList<int[]> output = new LinkedList<int[]>();
// add all intervals starting before newInterval
while (idx < n && newStart > intervals[idx][0])
output.add(intervals[idx++]);
// add newInterval
int[] interval = new int[2];
// if there is no overlap, just add the interval
if (output.isEmpty() || output.getLast()[1] < newStart)
output.add(newInterval);
// if there is an overlap, merge with the last interval
else {
interval = output.removeLast();
interval[1] = Math.max(interval[1], newEnd);
output.add(interval);
}
// add next intervals, merge with newInterval if needed
while (idx < n) {
interval = intervals[idx++];
int start = interval[0], end = interval[1];
// if there is no overlap, just add an interval
if (output.getLast()[1] < start) output.add(interval);
// if there is an overlap, merge with the last interval
else {
interval = output.removeLast();
interval[1] = Math.max(interval[1], end);
output.add(interval);
}
}
return output.toArray(new int[output.size()][2]);
}
}
复杂度分析:
方法二:
思路:
先考虑三种极端情况
- intervals为空
- newInterval[1] < intervals[0][0],直接插入第一个位置
- newInterval[0] > intervals[-1][1],直接插入最后一个位置
下面就要考虑重叠情况了
我们目标就是找到和newInterval相关那几个区间.
首先,左边,当newInterval[0] > intervals[i][1]说明没有和该区间没有重叠部分,继续遍历下一个区间,比如intervals = [[1,3],[6,9]], newInterval = [2,5]
然后,再看右边,这里有个情况,就是 当intervals[i][0] > newInterval[1]说明newInterval没有和任何区间重合,比如intervals = [[1,3],[6,9]], newInterval = [4,5],直接插入即可.
接下来我们要找右边重合区域,当while i < n and newInterval[1] >= intervals[i][0]说明有重叠部分,记录左边最大值!
最后把数组拼接一下即可!
上面就是我们思考过程,
接下来我们可以把过程简化,直接看代码,很好理解的
class Solution {
public int[][] insert(int[][] intervals, int[] newInterval) {
List<int[]> res = new ArrayList<>();
int i = 0;
while (i < intervals.length && newInterval[0] > intervals[i][1]) {
res.add(intervals[i]);
i++;
}
int[] tmp = new int[]{newInterval[0], newInterval[1]};
while (i < intervals.length && newInterval[1] >= intervals[i][0]) {
tmp[0] = Math.min(tmp[0], intervals[i][0]);
tmp[1] = Math.max(tmp[1], intervals[i][1]);
i++;
}
res.add(tmp);
while (i < intervals.length) {
res.add(intervals[i]);
i++;
}
return res.toArray(new int[0][]);
}
}