我正面临一个有趣的问题:
是否有可能“重叠”这些范围?也就是说,生成:
也许我可以让它更具图形化。这就是我的第一个:
a |------------------------------|
b |-------------------|
c |-----------------|
这是我想要获得的:
|------|---------|-------|-----|-----|
a a,c a,b,c a,b b
我找到了一种有效的解决方案,但这并不优雅:
您能想到更好的解决方案吗?我正在使用C#,但任何与语言无关的想法都会非常感激。谢谢!
答案 0 :(得分:10)
我会
我在解释它时确实做得不好。我很快就会为此编写一些代码。但在此之前,您的解决方案会发生什么:
a |------------------------------|
b |-------------------|
c |-----------------|
1. Start at day 1, add A to open ranges list, which now contains [A] 2. Move to the start of C. First RESULT RANGE: A range from Day 1 to C's start, with a value A. (what is in your open ranges list) 3. Add C to the open ranges list, which now contains [A,C] 4. Move to the start of B. Second RESULT RANGE: A range from C's start to B's start, with a value A,C. (what is in your open ranges list) 5. Add B to the open ranges list, which now contains [A,C,B] 6. Move to the end of C. Third RESULT RANGE: A range from B's start to C's end, with a value of A,C,B. 7. Remove C from the open ranges list, which now contains [A,B] 8. Move to the end of A. Fourth RESULT RANGE: A range from C's end to A's end, with a value of A,B 9. Remove A from the open ranges list, which now contains [B] 10. Move to the end of A. Fourth RESULT RANGE: A range from A's end to B's end, with a value of B RESULT RANGES 1. from Day 1 to C's start: A 2. from C's start to B's start: A,C 3. from B's start to C's end: A,C,B 4. from C's end to A's end: A,B 5. from A's end to B's end: B
你可以这样做:
以下是前几个步骤,使用示例数据+另一个范围D:
(“{处理”范围由**double stars**
表示)
a |------------------------------|
b |-------------------|
c |-----------------|
d |------------------------|
1. Process A Output ranges: |--------------A---------------| 2. Process B - Start of B is in **A**; split A in two: |-------A-------|------AB------| - End of B is after any output range range; creating new output range at end |-------A-------|------AB------|---B---| - Nothing was/is in between **A** and (nothing) 3. Process C - Start of C is in **A**; split A in two: |---A----|--AC--|------AB------|---B---| - End of C is in **AB**; split AB in two: |---A----|--AC--|--ABC--|--AB--|---B---| - There were/are no ranges between **A** and **AB** 4. Process D - Start of D is in **A**; split A in two: |-A-|-AD-|--AC--|--ABC--|--AB--|---B---| - End of D is in **AB**; split AB in two: |-A-|-AD-|--AC--|--ABC--|ABD|AB|---B---| - Ranges AC and ABC were/are in between **A** and **AB** |-A-|-AD-|--ACD-|-ABCD--|ABD|AB|---B---| Final output: |-A-|-AD-|--ACD-|-ABCD--|ABD|AB|---B---|
答案 1 :(得分:2)
您可能希望查看Interval Trees。
答案 2 :(得分:2)
我有代码可以做到这一点。它依赖于由from
然后to
排序的输入集(即,如果它是SQL,它将是ORDER BY from_value, to_value
),但之后它是非常优化的。
我的实现基本上执行@Justin L.的answer描述的内容,因此如果您只想要文本描述,请查看他对算法的回答。
代码在这里:LVK.DataStructures,您要查看的文件是:
使用它:
List<Range<DateTime>> ranges = ...
var slices = ranges.Slice();
这将为每个切片提供一个新范围,对于每个这样的范围,您将拥有一个.Data属性,其中包含返回到贡献范围的引用。
即。对于您的原始示例,您将获得您想要的,单个范围,以及.Data属性中的贡献范围a,b,c等。
这些类可能会使用我的类库的其他部分,而这些部分就在那里。如果您决定使用它,只需复制出您想要使用的部分。
如果您只对实施感兴趣,可以在RangeExtensions.cs文件line 447及其后的InternalSlice方法中找到它。
答案 3 :(得分:1)
你可以:
对于命名新范围,使用当前新范围包含的原始范围名称列表是有意义的,并且每次到达结束日期时,从列表中删除旧范围名称,并且每个范围都是点击开始日期,将其名称添加到列表中。
答案 4 :(得分:0)
这样做:
创建一个Event
类。
class DateEvent : IComparable<DateEvent>
{
public Date Date;
public int DateRangeId;
public bool IsBegin; // is this the start of a range?
public int CompareTo(DateEvent other)
{
if (Date < other.Date) return -1;
if (Date > other.Date) return +1;
if (IsBegin && !other.IsBegin) return -1;
if (!IsBegin && other.IsBegin) return +1;
return 0;
}
}
定义List<DateEvent> events;
将每个范围的开始和结束日期添加到列表中:
for (int i = 0; i < dateRanges.Length; ++i)
{
DateRange r = dateRanges[i];
events.Add(new DateEvent(r.BeginDate, i, true));
events.Add(new DateEvent(r.EndDate, i, false));
}
对事件进行排序。
events.Sort();
现在设置一个输出类:
class OutputDateRange
{
public Date BeginDate;
public Date EndDate;
public List<string> Names;
}
最后,遍历事件,保持存在的范围:
List<int> activeRanges;
List<OutputDateRange> output;
Date current = events[0].Date;
int i = 0;
while (i < events.Length;)
{
OutputDateRange out = new OutputDateRange();
out.BeginDate = current;
// Find ending date for this sub-range.
while (i < events.Length && events[i].Date == current)
{
out.EndDate = events[i].Date;
if (events[i].IsBegin)
activeRanges.Add(events[i].DateRangeId);
else
activeRanges.Remove(events[i].DateRangeId);
++i;
}
if (i < events.Length)
current = events[i].Date;
foreach (int j in activeRanges)
out.Names.Add(dateRanges[j].Name);
output.Add(out);
}
这应该可以解决问题。请注意,我没有制作构造函数,代码有点难看,但希望能传达出一般的想法。
答案 5 :(得分:0)