How to divide a set of overlapping ranges into non-overlapping ranges?

前端 未结 5 1101
伪装坚强ぢ
伪装坚强ぢ 2021-02-13 23:22

Let\'s say you have a set of ranges:

  • 0 - 100: \'a\'
  • 0 - 75: \'b\'
  • 95 - 150: \'c\'
  • 120 - 130: \'d\'

Obviously, these range

5条回答
  •  有刺的猬
    2021-02-13 23:40

    Pseudocode:

    unusedRanges = [ (each of your ranges) ]
    rangesInUse = []
    usedRanges = []
    beginningBoundary = nil
    
    boundaries = [ list of all your ranges' start and end values, sorted ]
    resultRanges = []
    
    for (boundary in boundaries) {
        rangesStarting = []
        rangesEnding = []
    
        // determine which ranges begin at this boundary
        for (range in unusedRanges) {
            if (range.begin == boundary) {
                rangesStarting.add(range)
            }
        }
    
        // if there are any new ones, start a new range
        if (rangesStarting isn't empty) {
            if (beginningBoundary isn't nil) {
                // add the range we just passed
                resultRanges.add(beginningBoundary, boundary - 1, [collected values from rangesInUse])
            }
    
            // note that we are starting a new range
            beginningBoundary = boundary
    
            for (range in rangesStarting) {
                rangesInUse.add(range)
                unusedRanges.remove(range)
            }
        }
    
        // determine which ranges end at this boundary
        for (range in rangesInUse) {
            if (range.end == boundary) {
                rangesEnding.add(range)
            }
        }
    
        // if any boundaries are ending, stop the range
        if (rangesEnding isn't empty) {
            // add the range up to this boundary
            resultRanges.add(beginningBoundary, boundary, [collected values from rangesInUse]
    
            for (range in rangesEnding) {
                usedRanges.add(range)
                rangesInUse.remove(range)
            }
    
            if (rangesInUse isn't empty) {
                // some ranges didn't end; note that we are starting a new range
                beginningBoundary = boundary + 1
            }
            else {
                beginningBoundary = nil
            }
        }
    }
    

    Unit test:

    At the end, resultRanges should have the results you're looking for, unusedRanges and rangesInUse should be empty, beginningBoundary should be nil, and usedRanges should contain what unusedRanges used to contain (but sorted by range.end).

提交回复
热议问题