What's a good, generic algorithm for collapsing a set of potentially-overlapping ranges?

后端 未结 10 2024
执念已碎
执念已碎 2020-12-08 00:28

I have a method that gets a number of objects of this class

class Range
{
    public T Start;
    public T End;
}

In my case

10条回答
  •  Happy的楠姐
    2020-12-08 01:15

    Tossing another hat into the ring. Very much the same implementation as Gary W's (from which I got the sorted list approach), but done as a test case and with some helpful functions added to the Range class.

    import java.util.ArrayList;
    import java.util.HashSet;
    import java.util.Set;
    
    import edu.emory.mathcs.backport.java.util.Collections;
    
    import junit.framework.TestCase;
    
    public class Range2Test extends TestCase {
        public void testCollapse() throws Exception {
            Set> set = new HashSet>();
            set.add(new Range(1, 5));
            set.add(new Range(3, 9));
            set.add(new Range(11, 15));
            set.add(new Range(12, 14));
            set.add(new Range(13, 20));
            Set> expected = new HashSet>();
            expected.add(new Range(1, 9));
            expected.add(new Range(11, 20));
            assertEquals(expected, collapse(set));
        }
    
        private static > Set> collapse(Set> ranges) {
            if (ranges == null)
                return null;
            if (ranges.size() < 2)
                return new HashSet>(ranges);
            ArrayList> list = new ArrayList>(ranges);
            Collections.sort(list);
            Set> result = new HashSet>();
            Range r = list.get(0);
            for (Range range : list) 
                if (r.overlaps(range)) {
                    r = r.union(range);
                } else {
                    result.add(r);
                    r = range;
                }
            result.add(r);
            return result;
        }
    
        private static class Range> implements Comparable> {
            public Range(T start, T end) {
                if (start == null || end == null)
                    throw new NullPointerException("Range requires start and end.");
                this.start = start;
                this.end = end;
            }
            public T    start;
            public T    end;
    
            private boolean contains(T t) {
                return start.compareTo(t) <= 0 && t.compareTo(end) <= 0;
            }
    
            public boolean overlaps(Range that) {
                return this.contains(that.start) || that.contains(this.start);
            }
    
            public Range union(Range that) {
                T start = this.start.compareTo(that.start) < 0 ? this.start : that.start;
                T end = this.end.compareTo(that.end) > 0 ? this.end : that.end;
                return new Range(start, end);
            }
    
            public String toString() {
                return String.format("%s - %s", start, end);
            }
    
            public int hashCode() {
                final int prime = 31;
                int result = 1;
                result = prime * result + end.hashCode();
                result = prime * result + start.hashCode();
                return result;
            }
    
            @SuppressWarnings("unchecked")
            public boolean equals(Object obj) {
            if (this == obj)                    return true;
            if (obj == null)                    return false;
            if (getClass() != obj.getClass())   return false;
            Range that = (Range) obj;
            return end.equals(that.end) && start.equals(that.start);
            }
    
            public int compareTo(Range that) {
                int result = this.start.compareTo(that.start);
                if (result != 0)
                    return result;
                return this.end.compareTo(that.end);
            }
        }
    }
    

提交回复
热议问题