以下是一个在Java中的O(N lg N)实现,它扩展了@Nikita Rybak提供的答案。
我的解决方案找到每个与至少另一个间隔重叠的间隔,并将它们都视为重叠间隔。例如,OP原始问题中的两个间隔(1,3)和(2,4)互相重叠,因此在这种情况下有2个重叠的间隔。换句话说,如果间隔A与间隔B重叠,则我将A和B都添加到重叠间隔的结果集中。
现在考虑间隔(1,100),(10,20)和(30,50)。我的代码将发现:
[ 10, 20] overlaps with [ 1, 100]
[ 30, 50] overlaps with [ 1, 100]
Resulting intervals that overlap with at least one other interval:
[ 1, 100]
[ 30, 50]
[ 10, 20]
为了防止
(1, 100)
被计算两次,我使用了一个 Java 的
Set
来仅保留唯一的区间对象。
我的解决方案如下:
- 按起始点对所有区间进行排序。此步骤为
O(N lg N)
。
- 跟踪
intervalWithLatestEnd
,即具有最新结束点的区间。
- 迭代排序列表中的所有区间。如果一个区间与
intervalWithLatestEnd
重叠,则将它们都添加到 Set 中。必要时更新 intervalWithLatestEnd
。此步骤为 O(N)
。
- 返回 Set(如果需要,转换为 List)。
总运行时间为
O(N lg N)
。它需要一个大小为
O(N)
的输出 Set。
实现
为了将区间添加到 Set 中,我创建了一个自定义 Interval 类,覆盖了
equals()
方法,如预期所示。
class Interval {
int start;
int end;
Interval(int s, int e) {
start = s; end = e;
}
@Override
public String toString() {
return String.format("[%3d, %3d]", start, end);
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + start;
result = prime * result + end;
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
final Interval other = (Interval) obj;
if (start != other.start)
return false;
if (end != other.end)
return false;
return true;
}
}
以下是运行该算法的代码:
private static List<Interval> findIntervalsThatOverlap(List<Interval> intervals) {
Set<Interval> set = new HashSet<Interval>();
Collections.sort(intervals, (x, y) -> Integer.compare(x.start, y.start));
Interval intervalWithLatestEnd = null;
for (Interval interval : intervals) {
if (intervalWithLatestEnd != null &&
interval.start < intervalWithLatestEnd.end) {
set.add(interval);
set.add(intervalWithLatestEnd);
System.out.println(interval + " overlaps with " +
intervalWithLatestEnd);
}
if (intervalWithLatestEnd == null ||
intervalWithLatestEnd.end < interval.end) {
intervalWithLatestEnd = interval;
}
}
return new ArrayList<Interval>(set);
}
测试用例
以下是一个测试用例,运行原始的间隔:
public static void testcase() {
List<Interval> intervals = null;
List<Interval> result = null;
intervals = new ArrayList<Interval>();
intervals.add(new Interval(1, 3));
intervals.add(new Interval(12, 14));
intervals.add(new Interval(2, 4));
intervals.add(new Interval(13, 15));
intervals.add(new Interval(5, 10));
result = findIntervalsThatOverlap(intervals);
System.out.println("Intervals that overlap with at least one other interval:");
for (Interval interval : result) {
System.out.println(interval);
}
}
得到以下结果:
[ 2, 4] overlaps with [ 1, 3]
[ 13, 15] overlaps with [ 12, 14]
Intervals that overlap with at least one other interval:
[ 2, 4]
[ 1, 3]
[ 13, 15]
[ 12, 14]
最后,这是一个更高级的测试案例:
public static void testcase() {
List<Interval> intervals = null;
List<Interval> result = null;
intervals = new ArrayList<Interval>();
intervals.add(new Interval(1, 4));
intervals.add(new Interval(2, 3));
intervals.add(new Interval(5, 7));
intervals.add(new Interval(10, 20));
intervals.add(new Interval(15, 22));
intervals.add(new Interval(9, 11));
intervals.add(new Interval(8, 25));
intervals.add(new Interval(50, 100));
intervals.add(new Interval(60, 70));
intervals.add(new Interval(80, 90));
result = findIntervalsThatOverlap(intervals);
System.out.println("Intervals that overlap with at least one other interval:");
for (Interval interval : result) {
System.out.println(interval);
}
}
结果如下:
[ 2, 3] overlaps with [ 1, 4]
[ 9, 11] overlaps with [ 8, 25]
[ 10, 20] overlaps with [ 8, 25]
[ 15, 22] overlaps with [ 8, 25]
[ 60, 70] overlaps with [ 50, 100]
[ 80, 90] overlaps with [ 50, 100]
Intervals that overlap with at least one other interval:
[ 2, 3]
[ 8, 25]
[ 9, 11]
[ 50, 100]
[ 1, 4]
[ 15, 22]
[ 10, 20]
[ 60, 70]
[ 80, 90]