efficient way to remove elements from list of ranges from a bigger range

brain storm :

I am looking for an efficient way to remove a list of ranges from a bigger range. The list of ranges will be contained with the bigger range

eg:

Bigger range: (0,10) 
List of Ranges:  [(2,7),(4,6),(6,8)]
expected result: {0,1,9,10}

I have an implementation below, but it is O(n2) and takes additional space of size O(n);

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/***
* input -> (0,10) and {(2,7),(4,6),{6,8}}
 * output -> {0,1,9,10}
 ***/
public class RemoveRanges {

    public static class Range {
        int start;
        int end;

        public Range(int x, int y){
            this.start = x;
            this.end = y;

        }
    }

    public static void main(String[] args) {

        Range outer = new Range(0,10);
        Range r1 = new Range(2,7);
        Range r2 = new Range(4,6);
        Range r3 = new Range(6,8);
        List<Range> rangesToBeRemoved = new ArrayList<>();
        rangesToBeRemoved.add(r1);
        rangesToBeRemoved.add(r2);
        rangesToBeRemoved.add(r3);

        System.out.println(removeRanges(outer, rangesToBeRemoved));

    }

    public static Set<Integer> removeRanges(Range outer, List<Range> rangesToBeRemoved ) {

        Set<Integer> outerElements = new HashSet<>();

        for (int i = outer.start; i<=outer.end;i++ ){
            outerElements.add(i);
        }

        for (Range range : rangesToBeRemoved) {
            for (int j = range.start; j<=range.end; j++) {
                outerElements.remove(j);
            }
        }
        return outerElements;
    }
}
hk6279 :

Refer to @Bohemian idea by changing your method from "add all element then remove by range" to "add element out of remove range"

  1. Sort the rangesToBeRemoved (by range.start)
  2. Loop over the range and add element that are not cover by ranges
  3. Add all element after the last range's end

    // assume rangesToBeRemoved has been sorted
    public static Set<Integer> addElementbyRemovedRanges(Range outer, List<Range> rangesToBeRemoved ) {
    
        Set<Integer> outerElements = new HashSet<Integer>();
    
        // this variable record the last element that has handled and act like a borderline
        int borderElementIndex = outer.start-1;
        for (Range range : rangesToBeRemoved) {
            if (range.end <= borderElementIndex ) {
                // omit this range as it has been cover by previous range(s)
                continue;
            }
    
            // add range if there is gap between range
            if (range.start > borderElementIndex ) {
                addElements(outerElements, borderElementIndex + 1, range.start - 1);
            }
    
            // update borderline
            borderElementIndex = range.end;
        }
        // Add all element after the last range's end
        addElements(outerElements, borderElementIndex + 1, outer.end);
    
        return outerElements;
    }
    
    public static void addElements(Set<Integer> outerElements, int start, int end) {
        if (start > end) {
            return;
        }
        for (int i=start; i<=end; i++){
            outerElements.add(i);
        }
    }
    

After sorting the rangesToBeRemoved, relationship between two ranges are

  1. Completely in range (e.g. (2,7) and (4,6))
  2. Partly in range (e.g. (2,7) and (6,8))
  3. Not in range (e.g. (2,3) and (6,8) || (2,3) and (4,8))

For case 1, ignore the second range. For case 2, update the borderline to second range's end. For case 3, add the gap to element list and update the borderline to second range's end.

The above code is trying to compare virtual range (outer.start-1, borderElementIndex) and all ranges in rangesToBeRemoved (sorted)

Reuse your example: {(2,7),(4,6),(6,8)}.

  • First, compare (-1,-1) with (2,7) and hit case 3, add the gap [0,1] into result set and change the borderElementIndex to 7.
  • Next, compare (-1,7) with (4,6) and hit case 1, ignore it.
  • Then, compare (-1,7) with (6,8) and hit case 2, change the borderElementIndex to 8.
  • Finally, append remaining gap [9,10] into result set.

To further reduce space usage, you could use the same idea state in @Danny_ds solution to store the range of element instead of individual elements.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=76244&siteId=1