重叠范围检查重叠

时间:2014-02-06 08:01:46

标签: c# .net list range intersect

我有一个范围列表,我想知道它们是否重叠。

我有以下代码。哪个似乎没有用。有没有更简单的方法来做到这一点或一种有效的方式:)

提前感谢任何建议。

   public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }

    private IList<Range> rangeList;

    private void Form1_Load(object sender, EventArgs e)
    {
        rangeList.Add(new Range{FromNumber = 0, ToNumber = 100});
        rangeList.Add(new Range { FromNumber = 101, ToNumber = 200 });

        // this range should over lap and throw an exception 
        rangeList.Add(new Range { FromNumber = 199, ToNumber = 300 });

    }

    private bool RangesOverlap()
    {
        var bigList = new List<List<int>>();

        foreach (var range in this.rangeList)
        {
            bigList.Add(new List<int> { range.FromNumber , range.ToNumber });
        }

        IEnumerable<IEnumerable<int>> lists = bigList;

        return lists
         .Where(c => c != null && c.Any())
         .Aggregate(Enumerable.Intersect)
         .ToList().Count > 0;
    }
}


public class Range
{
    public int FromNumber { get; set; }
    public int ToNumber { get; set; }
}

4 个答案:

答案 0 :(得分:8)

首先合并数字,然后检查生成的列表是按排序顺序:

rangeList
.OrderBy(p => p.FromNumber)
.Select(p => new[] { p.FromNumber, p.ToNumber })
.SelectMany(p => p)
.Aggregate((p, q) => q >= p ? q : int.MaxValue) == int.MaxValue

答案 1 :(得分:2)

在过去,我遇到了一个挑战,我必须为用户创建的范围(整数或实数)编写验证算法。我不得不检查3件事:

  1. 范围是连续的
  2. 范围不重叠
  3. LOW值必须始终

    所以我提出了以下PHP算法。

     //Let's hardcode an array of integers (it can be of reals as well):
     $range = array
     (
         array(1, 13),
         array(14, 20),
         array(21, 45),
         array(46, 50),
         array(51, 60)
     );
    
     //And the validation algorithm:
     $j = 0;
     $rows = sizeof($range);
     $step = 1;   // This indicates the step of the values.
                  // 1 for integers, 0.1 or 0.01 for reals
    
     for ($x=0; $x<$rows; $x++)
         for ($y=0; $y<$rows; $y++) {
             if ( ($range[$x][0] <= $range[$y][0]) AND ($range[$y][0] <= $range[$x][1]) AND ($x != $y) ) {
                 echo "Ranges intercepting";   // replace with error handling code
                 break 3;
             }
             if ( $range[$x][0] > $range[$x][1] ) {
                 echo "Not valid high & low";  // replace with error handling code
                 break 3;
             }
             if ( $range[$x][0] - $range[$y][1] == $step ) {
                 $j++;
             }
         }
     if ( $j != $rows - $step )
         echo "Not continuous";    // replace with error handling code
    

    我们遍历范围并成对比较它们。对于每一对我们:

    1. 找到重叠范围并引发错误
    2. 找到开始&amp;反向结束并引发错误
    3. 如果上述情况均未发生,我们会计算最终起点的差异。此数字必须等于范围数减去步骤(1,0.1,0.01等)。否则我们会引发错误。

      希望这有帮助!

答案 2 :(得分:0)

您可以通过略微修改RezaArabs答案来满足您的新要求:

rangeList
.Select(p => new[] { p.FromNumber, p.ToNumber })
.SelectMany(p => p.Distinct())
.Aggregate((p, q) => q >= p ? q : int.MaxValue) == int.MaxValue

答案 3 :(得分:0)

此问题的解决方案可以像编写自己的RangeList : IList<Range>一样简单,当{1}}方法指定的范围与集合中已有的一个或多个范围重叠时,Add()方法会抛出异常。

工作示例:

class Range
{
    public int FromNumber { get; set; }
    public int ToNumber { get; set; }

    public bool Intersects(Range range)
    {
        if ( this.FromNumber <= range.ToNumber )
        {
            return (this.ToNumber >= range.FromNumber);
        }
        else if ( this.ToNumber >= range.FromNumber )
        {
            return (this.FromNumber <= range.ToNumber);
        }

        return false;
    }
}

class RangeList : IList<Range>
{
    private readonly IList<Range> innerList;

    #region Constructors

    public RangeList()
    {
        this.innerList = new List<Range>();
    }

    public RangeList(int capacity)
    {
        this.innerList = new List<Range>(capacity);
    }

    public RangeList(IEnumerable<Range> collection)
    {
        if ( collection == null )
            throw new ArgumentNullException("collection");

        var overlap = from left in collection
                      from right in collection.SkipWhile(right => left != right)
                      where left != right
                      select left.Intersects(right);

        if ( overlap.SkipWhile(value => value == false).Any() )
            throw new ArgumentOutOfRangeException("collection", "The specified collection contains overlapping ranges.");

        this.innerList = new List<Range>(collection);
    }

    #endregion

    private bool IsUniqueRange(Range range)
    {
        if ( range == null )
            throw new ArgumentNullException("range");

        return !(this.innerList.Any(range.Intersects));
    }

    private Range EnsureUniqueRange(Range range)
    {
        if ( !IsUniqueRange(range) )
        {
            throw new ArgumentOutOfRangeException("range", "The specified range overlaps with one or more other ranges in this collection.");
        }

        return range;
    }

    public Range this[int index]
    {
        get
        {
            return this.innerList[index];
        }
        set
        {
            this.innerList[index] = EnsureUniqueRange(value);
        }
    }

    public void Insert(int index, Range item)
    {
        this.innerList.Insert(index, EnsureUniqueRange(item));
    }

    public void Add(Range item)
    {
        this.innerList.Add(EnsureUniqueRange(item));
    }

    #region Remaining implementation details

    public int IndexOf(Range item)
    {
        return this.innerList.IndexOf(item);
    }

    public void RemoveAt(int index)
    {
        this.innerList.RemoveAt(index);
    }

    public void Clear()
    {
        this.innerList.Clear();
    }

    public bool Contains(Range item)
    {
        return this.innerList.Contains(item);
    }

    public void CopyTo(Range[] array, int arrayIndex)
    {
        this.innerList.CopyTo(array, arrayIndex);
    }

    public int Count
    {
        get { return this.innerList.Count; }
    }

    public bool IsReadOnly
    {
        get { return this.innerList.IsReadOnly; }
    }

    public bool Remove(Range item)
    {
        return this.innerList.Remove(item);
    }

    public IEnumerator<Range> GetEnumerator()
    {
        return this.innerList.GetEnumerator();
    }

    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
    {
        return this.innerList.GetEnumerator();
    }

    #endregion
}

用法:

IList<Range> rangeList = new RangeList();

try
{
    rangeList.Add(new Range { FromNumber = 12, ToNumber = 12 });
    rangeList.Add(new Range { FromNumber = 13, ToNumber = 20 }); // should NOT overlap
    rangeList.Add(new Range { FromNumber = 12, ToNumber = 20 }); // should overlap
}
catch ( ArgumentOutOfRangeException exception )
{
    Console.WriteLine(exception.Message);
}