我有一个班级,
class A{
...
private Interval interval;
...
}
//maintaining epoch time
class Interval{
private long left;
private long right;
public Interval(long left, long right){
this.left = left;
this.right = right;
}
//getters and setters
}
我有一个对象列表List<A>
,我想对具有重叠(传递重叠,即a = b, b = c then a = c
)间隔的对象进行分组。假设我有4个对象,
A1 - Interval - 09:00 to 10:00
A2 - Interval - 13:00 to 14:00
A3 - Interval - 10:10 to 12:00
A4 - Interval - 09:30 to 10:30
程序的结果应该给我2个列表,
List1 - A1,A3,A4
List2 - A2
有关解决此问题和/或伪代码的任何建议吗?
答案 0 :(得分:0)
对于冗长而感到抱歉,但我相信这个问题值得:-)。该解决方案的间隔数为 n * log(n)。发生这种复杂性的位置在间隔的排序范围内。请注意,此解决方案需要注意包含一个间隔完全包含在另一个间隔内的情况。结果组的子间隔反向排序,但如果您愿意,可以通过在结尾处反转列表来轻松解决此问题。我不建议通过在开头而不是结尾插入连接的间隔来修复它,因为如果使用基于ArrayList的实现,这会将复杂性增加到O(n ^ 2)。我想你可能可以使用LinkedList并在开头插入,但这可能会在其他地方伤害你,这取决于你在形成它们后对这些组做了什么。
public static long max(long a, long b){
return (a>b ? a : b);
}
public static long min(long a, long b){
return (a<b ? a : b);
}
/**
* A meta interval is composed of sub intervals. Its left endpoint is
* the leftmost point of its subintervals and its right endpoint is
* the rightmost point of its subintervals.
*/
static class MetaInterval extends Interval{
/**
* meta intervals are composed of lists of subintervals.
*/
private List<Interval> subintervals;
public MetaInterval(Interval initialSubInterval){
super(initialSubInterval.getLeft(),initialSubInterval.getRight(),null);
this.subintervals = new ArrayList<Interval>();
this.subintervals.add(initialSubInterval);
}
public void join(MetaInterval other){
verifyOverlap(other);
this.setLeft(min(this.getLeft(),other.getLeft()));
this.setRight(max(this.getRight(),other.getRight()));
this.subintervals.addAll(other.getSubintervals());
other.invalidate();
}
public void setSubintervals(List<Interval> subintervals){
this.subintervals = subintervals;
}
public List<Interval> getSubintervals(){
return this.subintervals;
}
private void invalidate(){
this.setSubintervals(null);
this.setLeft(0);
this.setRight(0);
}
public boolean isInvalid(){
return this.getSubintervals()==null;
}
}
//maintaining epoch time
static class Interval implements Comparable<Interval>{
private long left;
private long right;
private String intervalId;
public Interval(long left, long right, String intervalId){
this.left = left;
this.right = right;
this.intervalId = intervalId;
}
public boolean pointIsIn(long point){
return (point>=this.getLeft() && point<=this.getRight());
}
public long getLeft(){
return left;
}
public long getRight(){
return right;
}
public void setLeft(long left){
this.left = left;
}
public void setRight(long right){
this.right = right;
}
public String getIntervalId(){
return intervalId;
}
public boolean overlaps(Interval other){
return pointIsIn(other.getLeft()) || pointIsIn(other.getRight()) || other.pointIsIn(getLeft()) || other.pointIsIn(getRight());
}
public void verifyOverlap(Interval other){
if(!overlaps(other)){
throw new IllegalStateException("Other interval does not overlap");
}
}
/**
* Sort by leftmost part of interval.
*/
@Override
public int compareTo(Interval o) {
return Long.compare(this.getLeft(), o.getLeft());
}
public String toString(){
return intervalId+":["+getLeft()+","+getRight()+"]";
}
}
/**
* Given a list of intervals, returns a list of interval groups where
* the intervals in the groups all overlap. So if A overlaps with B and
* B with C, then A,B,and C will be returned in a group. Supposing intervals
* D and E share nothing with A, B, or C, but do share with each other, D and E
* will be returned as a separate group from A,B,C.
* @param baseIntervals
* @return
*/
public static List<List<Interval>> getOverlappingGroups(List<Interval> baseIntervals){
List<MetaInterval> baseMetaIntervals = toMetaIntervals(baseIntervals);
List<MetaInterval> mergedMetaIntervals = getMergedMetaIntervals(baseMetaIntervals);
List<List<Interval>> intervalGroups = metaIntervalsToGroups(mergedMetaIntervals);
return intervalGroups;
}
private static List<MetaInterval> getMergedMetaIntervals(
List<MetaInterval> metaIntervals) {
if(metaIntervals.isEmpty()){
return metaIntervals;
}
//order the meta intervals by their starting point.
Collections.sort(metaIntervals);
//go through and merge the overlapping meta intervals.
//This relies on the logic that if interval i overlaps with
//an interval that started before it, then once all the intervals
//before i have been merged, interval i will have a starting point
//consecutive to the starting point of the the preceeding interval.
for(int i=0; i< metaIntervals.size()-1; i++){
MetaInterval thisInterval = metaIntervals.get(i);
MetaInterval nextInterval = metaIntervals.get(i+1);
if(thisInterval.overlaps(nextInterval)){
nextInterval.join(thisInterval);
}
}
List<MetaInterval> resultIntervals = new ArrayList<MetaInterval>();
//All intervals from the original list either:
//(a) didn't overlap with any others
//(b) overlapped with others and were chosen to represent the merged group or
//(c) overlapped with others, were represented in the group in another meta
// interval, and then marked as invalid.
//Go through and only add the valid intervals to be returned.
for(MetaInterval i : metaIntervals){
if(!i.isInvalid()){
resultIntervals.add(i);
}
}
return resultIntervals;
}
/**
* Convert a list of meta intervals into groups of intervals.
* @param mergedMetaIntervals
* @return
*/
private static List<List<Interval>> metaIntervalsToGroups(
List<MetaInterval> mergedMetaIntervals) {
List<List<Interval>> groups = new ArrayList<>(mergedMetaIntervals.size());
for(MetaInterval metaInterval : mergedMetaIntervals){
groups.add(metaInterval.getSubintervals());
}
return groups;
}
private static List<MetaInterval> toMetaIntervals(
List<Interval> baseIntervals) {
ArrayList<MetaInterval> metaIntervals = new ArrayList<MetaInterval>(baseIntervals.size());
for(Interval i : baseIntervals){
metaIntervals.add(new MetaInterval(i));
}
return metaIntervals;
}
public static void main(String[] args){
Interval a = new Interval(20, 30, "A");
Interval b = new Interval(21,22,"B");
Interval c = new Interval(500,503,"C");
Interval d = new Interval(28,38, "D");
Interval e = new Interval(490,502,"E");
//note: A,B, and D are an overlapping group, and
//D and E are another.
List<List<Interval>> intervalGroups = getOverlappingGroups(Arrays.asList(a,b,c,d,e));
for(List<Interval> group : intervalGroups){
System.out.println(group.toString());
}
}
输出:
[D:[28,38], B:[21,22], A:[20,30]]
[C:[500,503], E:[490,502]]
答案 1 :(得分:0)
定义set<Folder*>
类(我没有制作getter / setter - 我很懒惰):
Interval
现在我们可以运行这个程序:
class Interval {
public long left;
public long right;
public String name;
public Interval(String name, long left, long right) {
this.name = name;
this.left = left;
this.right = right;
}
public boolean overlaps(Interval other) {
return (this.left >= other.left && this.left <= other.right)
|| (this.right <= other.right && this.right >= other.left)
|| (other.left >= this.left && other.left <= this.right)
|| (other.right <= this.right && other.right >= this.left);
}
}
哪个输出:
public class Main {
public static void main(String[] args) {
ArrayList<Interval> all = new ArrayList<Interval>();
all.add(new Interval("A",0,10));
all.add(new Interval("B",30,40));
all.add(new Interval("C",5,15));
all.add(new Interval("D",15,30));
// We run our algorithm...
ArrayList<ArrayList<Interval>> result = new ArrayList<ArrayList<Interval>>();
for (Interval interval : all) {
if (result.isEmpty()) {
result.add(new ArrayList<Interval>());
result.get(0).add(interval);
}else{
boolean addedSomewhere = false;
for (ArrayList<Interval> group : result) {
for (Interval other : group) {
if (other.overlaps(interval)) {
group.add(interval);
addedSomewhere = true;
}
if (addedSomewhere) break;
}
if (addedSomewhere) break;
}
if (!addedSomewhere) {
result.add(new ArrayList<Interval>());
result.get(result.size() - 1).add(interval);
}
}
}
// Print the result...
for (ArrayList<Interval> group : result) {
System.out.print("{");
for (Interval interval : group) {
System.out.print("[" + interval.name + "," + interval.left + "," + interval.right + "]");
}
System.out.print("}\n");
}
}
}