如何找到所有重叠的范围并将它们分成块?

时间:2015-05-27 03:19:35

标签: javascript algorithm

我有一系列范围,我希望能够找到所有重叠范围:

例如:

var examples = [
    // Group 1
    {start: 9, end: 10.50},       // Range 1
    {start: 10, end: 10.50},      // Range 5

    // Group 2
    {start: 11, end: 13},         // Range 2
    {start: 13.5, end: 14.5},     // Range 3
    {start: 11.5, end: 14}        // Range 4
]
  1. 范围2与范围4重叠
  2. 范围3与范围4重叠
  3. 虽然范围2与范围3不重叠,但因为它们都与范围4重叠。它们将被放入同一组
  4. 范围1和范围5仅相互重叠,因此它们将位于各自的组中
  5. JSFiddle:

    http://jsfiddle.net/jukufon7/2/

3 个答案:

答案 0 :(得分:4)

以下是我的观点:

jsfiddle

var examples = [
    {start: 17, end: 20},
    {start: 9, end: 10.50},
    {start: 15, end: 17},
    {start: 11, end: 12},
    {start: 18, end: 19.5},
    {start: 19.5, end: 22},
    {start: 11.5, end: 12.5},
    {start: 11.5, end: 13},
    {start: 17.5, end: 18.5},
    {start: 19, end: 19.5},
    {start: 22, end: 25}
]

function partitionIntoOverlappingRanges(array) {
  array.sort(function (a,b) {
    if (a.start < b.start)
      return -1;
    if (a.start > b.start)
      return 1;
    return 0;
  });
  var getMaxEnd = function(array) {
    if (array.length==0) return false;
    array.sort(function (a,b) {
      if (a.end < b.end)
        return 1;
      if (a.end > b.end)
        return -1;
      return 0;
    });
    return array[0].end;    
  };
  var rarray=[];
  var g=0;
  rarray[g]=[array[0]];

  for (var i=1,l=array.length;i<l;i++) {
    if ( (array[i].start>=array[i-1].start)
         &&
         (array[i].start<getMaxEnd(rarray[g]))
    ) {    
      rarray[g].push(array[i]);
    } else {
      g++;   
      rarray[g]=[array[i]];
    }
  }
  return rarray;
} // end partitionIntoOverlappingRanges

上述examples的结果

results

答案 1 :(得分:2)

这是一个简单的扫描算法。它在O(n log n)中执行,因为必须对范围进行排序。

基本思路是从左向右扫描,查找起点和终点(需要每个点的排序列表)。扫描时,跟踪活动范围的数量(即,遇到起点并且尚未遇到其端点的范围)。每次到达起点时,都需要将范围添加到当前组。通过在每个起始点递增它并在每个终点递减它来维持范围计数。每次计数返回0时,都会找到一个完整的组。

如果要计算简化的范围集而不是组,则可以简化。当有效范围计数从0递增到1时,不设置组中的一组范围,而是设置当前组合组的起始点,当有效范围计数从1递减到0时,设置结束点。在这种情况下,您只需要一个起始点的排序列表和一个排序的终点列表(在所示的算法中,通过按起点对范围本身进行排序来找到排序的起始点。需要该组以便范围可以被添加到累积组。)

  1. 按照起始值对范围进行排序。

  2. 列出结束值并对其进行排序(不必知道哪个范围属于端点)。调用此end_values。

  3. 将current_group初始化为空集,并将active_range_count初始化为0.将current_range和current_end初始化为0.

  4. 循环直到完成:

    1. 如果current_range是范围和范围的有效索引[current_range] .start小于end_values [current_end]:

      • 将范围[current_range]添加到current_group,增加current_range并增加active_range_count。
      • 循环。
    2. 否则,如果current_end是end_values的有效索引:

      • 递减active_range_count并递增current_end。
      • 如果active_range_count为0,则current_group完成;保存它,并将current_group重新初始化为空集。
      • 循环。
    3. 否则,完成。

  5. 以下是javascript中的两个版本:

    /* Partition an array of ranges into non-overlapping groups */
    /* Side-effect: sorts the array */
    function partition(ranges) {
      var end_values = ranges.map(function(r){return r.end}).sort(function(a, b){return a - b})
      ranges.sort(function(a, b){return a.start - b.start})
      var i = 0, j = 0, n = ranges.length, active = 0
      var groups = [], cur = []
      while (1) {
        if (i < n && ranges[i].start < end_values[j]) {
          cur.push(ranges[i++])
          ++active
        } else if (j < n) {
          ++j  
          if (--active == 0) {
            groups.push(cur)
            cur = [] 
          }    
        } else break   
      }
      return groups
    }
    
    /* Given a array of possibly overlapping ranges, produces
     * an array of non-overlapping ranges covering the same
     * values.
     */
    function compose_ranges(ranges) {
      var starts = ranges.map(function(r){return r.start}).sort(function(a, b){return a - b})
      var ends = ranges.map(function(r){return r.end}).sort(function(a, b){return a - b})
      var i = 0, j = 0, n = ranges.length, active = 0
      var combined = []
      while (1) {
        if (i < n && starts[i] < ends[j]) {
          if (active++ == 0) combined.push({start: starts[i]})
          ++i
        } else if (j < n) {
          if (--active == 0) combined[combined.length - 1].end = ends[j]
          ++j
        } else break;
      } 
      return combined
    } 
    

答案 2 :(得分:0)

概念很简单:记录每个特定组的最大范围。

点击下面的代码。

function contains(range, number) {
    return number > range.start && number < range.end
}


function partitionIntoOverlappingRanges(array) {
    var groups = [];
    for (var i = 0; i < array.length; i++) {
        for (var j = 0; j < groups.length; j++) {
            if (contains(groups[j], array[i].start) || contains(groups[j], array[i].end)) {
                groups[j].arrays.push(array[i]);
                if (groups[j].start > array[i].start) groups[j].start = array[i].start;
                if (groups[j].end < array[i].end) groups[j].end = array[i].end;
                break;
            }
        }
        if (j == groups.length) {
            groups.push({
                start: array[i].start,
                end: array[i].end,
                arrays: [array[i]]
            })
        }
    }
    return groups
}