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

后端 未结 10 2019
执念已碎
执念已碎 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 could probably be optimized...

    using System.Collections.Generic;
    using System.Linq;
    using System;
    static class Range
    {
        public static Range Create(T start, T end)
        {
            return new Range(start, end);
        }
        public static IEnumerable> Normalize(
            this IEnumerable> ranges)
        {
            return Normalize(ranges, null);
        }
        public static IEnumerable> Normalize(
            this IEnumerable> ranges, IComparer comparer)
        {
            var list = ranges.ToList();
            if (comparer == null) comparer = Comparer.Default;
            for (int i = list.Count - 1; i >= 0; i--)
            {
                var item = list[i];
    
                for (int j = 0; j < i; j++)
                {
                    Range? newValue = TryMerge(comparer, item, list[j]);
    
                    // did we find a useful transformation?
                    if (newValue != null)
                    {
                        list[j] = newValue.GetValueOrDefault();
                        list.RemoveAt(i);
                        break;
                    }
                }
            }
            list.Sort((x, y) =>
            {
                int t = comparer.Compare(x.Start, y.Start);
                if (t == 0) t = comparer.Compare(x.End, y.End);
                return t;
            });
            return list.AsEnumerable();
        }
    
        private static Range? TryMerge(IComparer comparer, Range item, Range other)
        {
            if (comparer.Compare(other.End, item.Start) == 0)
            { // adjacent ranges
                return new Range(other.Start, item.End);
            }
            if (comparer.Compare(item.End, other.Start) == 0)
            { // adjacent ranges
                return new Range(item.Start, other.End);
            }
            if (comparer.Compare(item.Start, other.Start) <= 0
                && comparer.Compare(item.End, other.End) >= 0)
            { // item fully swalls other
                return item;
            }
            if (comparer.Compare(other.Start, item.Start) <= 0
                && comparer.Compare(other.End, item.End) >= 0)
            { // other fully swallows item
                return other;
            }
            if (comparer.Compare(item.Start, other.Start) <= 0
                && comparer.Compare(item.End, other.Start) >= 0
                && comparer.Compare(item.End, other.End) <= 0)
            { // partial overlap
                return new Range(item.Start, other.End);
            }
            if (comparer.Compare(other.Start, item.Start) <= 0
                 && comparer.Compare(other.End, item.Start) >= 0
                && comparer.Compare(other.End, item.End) <= 0)
            { // partial overlap
                return new Range(other.Start, item.End);
            }
            return null;
        }
    }
    public struct Range
    {
        private readonly T start, end;
        public T Start { get { return start; } }
        public T End { get { return end; } }
        public Range(T start, T end)
        {
            this.start = start;
            this.end = end;
        }
        public override string ToString()
        {
            return start + " to " + end;
        }
    }
    
    static class Program
    {
        static void Main()
        {
            var data = new[] 
            {
                Range.Create(1,5), Range.Create(3,9),
                Range.Create(11,15), Range.Create(12,14),
                Range.Create(13,20)
            };
            var result = data.Normalize();
            foreach (var item in result)
            {
                Console.WriteLine(item);
            }
        }
    }
    

提交回复
热议问题