基于投影点对时间间隔进行分组的最佳方法

时间:2014-09-05 21:44:10

标签: algorithm sorting

想象一下,我们已经对时间间隔列表进行了排序(按开始时间排序)。 我正在寻找项目的最佳解决方案'这些区间到轴,结果是一组对象,描述:投影间隔开始和结束时间以及落入投影间隔的源区间数组。

让我举例解释一下:假设我们有4个区间作为输入(按开始时间排序,然后按结束时间排序):

   [---R1---)    
         [-----R2-----)
         [---------R3-------)
                 [----R4----)

 --|-----|--|----|----|-----|---> t (time axis)
      1    3   2    3    2 

在这种情况下,我希望获得5个元素的数组,每个元素都是一个描述间隔开始/结束的对象和一个源间隔列表。图表上的轴下方的数字显示该列表中的项目数。

请帮助我找到解决此任务的最快方法

2 个答案:

答案 0 :(得分:1)

这样的东西?

def groupIntervals(intervals):
    events = {}
    for start, stop, name in intervals:
        if start not in events: events[start] = []
        events[start].append(('start', name))
        if stop not in events: events[stop] = []
        events[stop].append(('stop', name))
    last = None
    output = []
    active = set()
    for time in sorted(events.keys()):
        if active and last is not None:
            output.append((last, time, active.copy()))
        last = time
        for action, name in events[time]:
            if action == 'start': active.add(name)
            elif action == 'stop': active.remove(name)
            else: assert False
    return output

使用示例:

>>> groupIntervals([(1, 3, 'R1'), (2, 5, 'R2'), (2, 6, 'R3'),
...                 (4, 6, 'R4')])
[(1, 2, set(['R1'])),
 (2, 3, set(['R1', 'R2', 'R3'])),
 (3, 4, set(['R2', 'R3'])),
 (4, 5, set(['R4', 'R2', 'R3'])),
 (5, 6, set(['R4', 'R3']))]

C ++版本,使用更清晰的数据结构。

#include <cstdio>
#include <limits>
#include <list>
#include <queue>
#include <string>
#include <vector>

struct Interval {
  Interval(std::string name, int start, int stop);
  std::string name;
  int start;
  int stop;
};

Interval::Interval(std::string name, int start, int stop)
    : name(name), start(start), stop(stop) {
}

typedef std::list<std::vector<Interval>::const_iterator> ActiveList;

struct StopEvent {
  StopEvent(int stop, ActiveList::iterator j);
  int stop;
  ActiveList::iterator j;
};

StopEvent::StopEvent(int stop, ActiveList::iterator j)
    : stop(stop), j(j) {
}

struct StopEventGreater {
  bool operator()(StopEvent const& a,
                  StopEvent const& b) const;
};

bool StopEventGreater::operator()(StopEvent const& a,
                                  StopEvent const& b) const {
  return a.stop > b.stop;
}

void Sweep(std::vector<Interval> const& intervals) {
  std::vector<Interval>::const_iterator i(intervals.begin());
  std::priority_queue<StopEvent,
      std::vector<StopEvent>,
      StopEventGreater> active_queue;
  ActiveList active_list;
  int last_time(std::numeric_limits<int>::min());
  while (i != intervals.end() || !active_queue.empty()) {
    bool start(i != intervals.end() &&
               (active_queue.empty() || i->start < active_queue.top().stop));
    int time(start ? i->start : active_queue.top().stop);
    if (time != last_time && !active_list.empty()) {
      std::printf("[%d, %d):", last_time, time);
      for (ActiveList::const_iterator j(active_list.begin());
           j != active_list.end();
           ++j) {
        std::printf(" %s", (*j)->name.c_str());
      }
      std::putchar('\n');
    }
    last_time = time;
    if (start) {
      active_queue.push(StopEvent(i->stop,
                                  active_list.insert(active_list.end(), i)));
      ++i;
    } else {
      active_list.erase(active_queue.top().j);
      active_queue.pop();
    }
  }
}

int main(void) {
  std::vector<Interval> intervals;
  intervals.push_back(Interval("R1", 0, 4));
  intervals.push_back(Interval("R2", 1, 9));
  intervals.push_back(Interval("R3", 1, 11));
  intervals.push_back(Interval("R4", 6, 11));
  Sweep(intervals);
}

答案 1 :(得分:0)

最后我找到了最有效的方法。它使用一个排序操作和O(N * 2)次迭代来构建结果。

public IEnumerable<DateProjectedItems<T>> Project(IList<T> items)
{
    if (items.Count <= 1)
    {
        if (items.Count == 0)
        {
            yield break;
        }
        yield return new DateProjectedItems<T>
        {
            DateRange = items[0].DateRange,
            Items = items
        };
    }
    else
    {
        var endOrdered = items.OrderBy(i => i.DateRange.DateTimeTo).ToList();
        var active = new List<T>();
        DateTime? last = null;                
        foreach (var pair in TwoArrayIterator(items, endOrdered))
        {
            DateTime current = pair.Key == 1 ? pair.Value.DateRange.DateTimeFrom : pair.Value.DateRange.DateTimeTo;
            if (last != null && current != last)
            {
                yield return new DateProjectedItems<T>
                {
                    DateRange = new DateRange(last.Value, current),
                    Items = active.ToList()
                };
            }
            if (pair.Key == 1)
            {
                active.Add(pair.Value);
            }
            else
            {
                active.Remove(pair.Value);
            }
            last = current;
        }             
    }
}

public IEnumerable<KeyValuePair<int, T>> TwoArrayIterator(IList<T> arr1, IList<T> arr2)
{
    var index1 = 0;
    var index2 = 0;
    while (index1 < arr1.Count || index2 < arr2.Count)
    {
        if (index1 >= arr1.Count)
            yield return new KeyValuePair<int, T>(2, arr2[index2++]);
        else if (index2 >= arr2.Count)
            yield return new KeyValuePair<int, T>(1, arr1[index1++]);
        else
        {
            var elt1 = arr1[index1];
            var elt2 = arr2[index2];
            if (elt1.DateRange.DateTimeFrom < elt2.DateRange.DateTimeTo)
            {
                index1++;
                yield return new KeyValuePair<int, T>(1, elt1);
            }
            else
            {
                index2++;
                yield return new KeyValuePair<int, T>(2, elt2);
            }
        }
    }
}