What's a good, generic algorithm for collapsing a set of potentially-overlapping ranges?

后端 未结 10 2038
执念已碎
执念已碎 2020-12-08 00:28

I have a method that gets a number of objects of this class

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

In my case

10条回答
  •  醉话见心
    2020-12-08 01:06

    This seems to works and is easy to understand.

        public static IEnumerable> Collapse(this IEnumerable> me, IComparer comparer)
        {
            List> orderdList = me.OrderBy(r => r.Start).ToList();
            List> newList = new List>();
    
            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 { Start = min, End = max });
                    min = item.Start;
                }
                max = comparer.Compare(max, item.End) > 0 ? max : item.End;
            }
            newList.Add(new Range{Start=min,End=max});
    
            return newList;
        }
    

    Here is the variation which I mentioned in the comments. It's basically the same thing, but with some checking and yielding of the results instead of collecting in a list before returning.

        public static IEnumerable> Collapse(this IEnumerable> ranges, IComparer comparer)
        {
            if(ranges == null || !ranges.Any())
                yield break;
    
            if (comparer == null)
                comparer = Comparer.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);
        }
    

提交回复
热议问题