小编典典

有什么好的通用算法可以折叠一组可能重叠的范围?

algorithm

我有一个方法可以获取此类的许多对象

class Range<T>
{
    public T Start;
    public T End;
}

在我的情况下TDateTime,但int为了简单起见,请使用。我想要一种将这些范围折叠成覆盖相同“区域”但不重叠的方法。

所以如果我有以下范围

  • 1到5
  • 3至9
  • 11至15
  • 12至14
  • 13至20

该方法应该给我

  • 1至9
  • 11至20

猜猜它会被称为工会吗?我想方法签名可能看起来像这样:

public static IEnumerable<Range<T>> Collapse<T>(
    this IEnumerable<Range<T>>, 
    IComparable<T> comparer)
{
    ...
}

我在这里查看了一些其他类似的问题,但尚未找到该问题的实现。和其他相同问题的答案描述了算法,但是我不确定我是否理解算法。也不是特别擅长实现算法,所以我希望这里有人可以帮助我。


阅读 189

收藏
2020-07-28

共1个答案

小编典典

这似乎可行并且很容易理解。

    public static IEnumerable<Range<T>> Collapse<T>(this IEnumerable<Range<T>> me, IComparer<T> comparer)
    {
        List<Range<T>> orderdList = me.OrderBy(r => r.Start).ToList();
        List<Range<T>> newList = new List<Range<T>>();

        T max = orderdList[0].End;
        T min = orderdList[0].Start;

        foreach (var item in orderdList.Skip(1))
        {
            if (comparer.Compare(item.End, max) > 0 && comparer.Compare(item.Start, max) > 0)
            {
                newList.Add(new Range<T> { Start = min, End = max });
                min = item.Start;
            }
            max = comparer.Compare(max, item.End) > 0 ? max : item.End;
        }
        newList.Add(new Range<T>{Start=min,End=max});

        return newList;
    }

这是我在评论中提到的变体。基本上是同一回事,但是要进行一些检查和得出结果,而不是在返回之前将其收集在列表中。

    public static IEnumerable<Range<T>> Collapse<T>(this IEnumerable<Range<T>> ranges, IComparer<T> comparer)
    {
        if(ranges == null || !ranges.Any())
            yield break;

        if (comparer == null)
            comparer = Comparer<T>.Default;

        var orderdList = ranges.OrderBy(r => r.Start);
        var firstRange = orderdList.First();

        T min = firstRange.Start;
        T max = firstRange.End;

        foreach (var current in orderdList.Skip(1))
        {
            if (comparer.Compare(current.End, max) > 0 && comparer.Compare(current.Start, max) > 0)
            {
                yield return Create(min, max);
                min = current.Start;
            }
            max = comparer.Compare(max, current.End) > 0 ? max : current.End;
        }
        yield return Create(min, max);
    }
2020-07-28