在序列中查找缺失和重叠的数字

时间:2011-08-11 10:06:06

标签: c# linq range sequence

假设我们有这样的数据结构:

var sequences = new List<Tuple<int, int>>
                {
                    new Tuple<int, int>(1, 10),
                    new Tuple<int, int>(8, 101),
                    new Tuple<int, int>(102, 103),
                    new Tuple<int, int>(104, 104),
                    new Tuple<int, int>(110, 200)
                };

我想从这个系列中得到两个结果:

  • 所有缺失的数字(在此示例中:105,106,107,108,109)
  • 所有重叠的数字(在此示例中为:8,9,10)

我可以编写一个带有几个循环和辅助集合的算法。这个过程是有效的,但我想知道这是否可以在LINQ和/或其他更简单和更简短的算法的帮助下实现?

编辑: 我上面的例子中的数据结构代表5个序列,第一个包含1到10的数字,第二个包含从8到101的数字,依此类推...因为在生产中序列可以更大(向上)对于数百万),它们不用实际集合表示(例如带有所有数字的列表),而是用元组表示,它代表每个序列的最小和最大数量。

5 个答案:

答案 0 :(得分:5)

你可以通过

来做
var missing = 
      Enumerable.Range(1, 200)
               .Where(i => sequences.All(t => t.Item1 > i || t.Item2 < i));
var overlapping = 
      Enumerable.Range(1, 200)
                .Where(i => sequences.Count(t => t.Item1 <= i && t.Item2 >= i) > 1);

答案 1 :(得分:2)

我知道这个问题的算法(它是伪代码)。 (复杂性类O(nlog(n)),其中n是元组的数量)

因此解决方案是按函数排序元组:

  int comparer( Tuple a, Tuple b) {
      if ( a.first.compareTo(b.first) == 0 ) {
          return a.second.compareTo(b.second);
      } else 
          return a.first.compareTo(b.first);
  }

所以示例元组:(1,10),(1,5),(2,8)将排序为: (1,5),(1,10),(2,8)。

下一步是累积此结果。迭代这个结果并且:

 Tuple result = SortedList[0];
 foreach ( Tuple tuple in SortedList ) {

     if ( result.second < tuple.first ) {

        // here you have missing number (result.second, tuple.first)

        result.first = tuple.first; 
        result.second = tuple.second
     } else if ( result.second > tuple.first ) {

        // here you have overlapping number (tuple.first, min( result.second,tuple.second ))

        if ( result.second < tuple.second ) {
              result.second = tuple.second;
        }
     } else {
        result.second = tuple.second;
     }

 }

我们知道,如果将迭代下一个元组第一个数字大于或等于结果。第一个。在代码中注释会告诉您重叠和缺少数字的位置

答案 2 :(得分:1)

试试这个

var expandedSequences = sequences.Select(t => Enumerable.Range(t.Item1, t.Item2-t.Item1)).SelectMany(t => t).OrderBy(i => i);
var dupes = expandedSequences.GroupBy(i => i).Where(g => g.Count() > 1).Select(g => g.Key);
var missing = Enumerable.Range(expandedSequences.Min(), expandedSequences.Max()).Except(expandedSequences);

答案 3 :(得分:1)

一次通过:

var sequences = new List<Tuple<int, int>>
    {
        new Tuple<int, int>(1, 10),
        new Tuple<int, int>(8, 101),
        new Tuple<int, int>(102, 103),
        new Tuple<int, int>(104, 104),
        new Tuple<int, int>(110, 200)
    };
var missing = new List<int>();
var overlap = new List<int>();

sequences.Aggregate((prev, current) => {
    if (prev.Item2 >= current.Item1) {
        overlap.AddRange(Enumerable.Range(current.Item1, prev.Item2 - current.Item1 + 1));
    }
    if (current.Item1 > prev.Item2 + 1) {
        missing.AddRange(Enumerable.Range(prev.Item2 + 1, current.Item1 - prev.Item2 - 1));
    }
    return current;
});

答案 4 :(得分:1)

有一些边缘情况我只能假设你想要处理的方式。我选择不处理其中一个(在代码中评论)。由于您没有说明您希望如何表示缺失/重排序列,因此我使用元组选择了您自己的格式来识别序列的开始和结束。

//Assumes they are sorted on item1
        Tuple<IEnumerable<Tuple<int,int>>,IEnumerable<Tuple<int,int>>> FindMissingAndOverLapping(IEnumerable<Tuple<int,int>> sequences){
            var previous = Tuple.Create(0, 0);
            var missing = new List<Tuple<int,int>>();
            var overlapping = new List<Tuple<int, int>>();
            var max = 0;
            foreach (var sequence in sequences){
                var end = previous.Item2;
                max = end > max ? end : max;
                if (previous.Item2 < sequence.Item1 + 1){
                    missing.Add(Tuple.Create(previous.Item2 + 1, sequence.Item1 - 1));
                } else if (max < sequence.Item1){
                    overlapping.Add(Tuple.Create(sequence.Item1, max));
                }
            }
            //The sequences in ovrelapping can be ovrelapping them self
            return new Tuple<IEnumerable<Tuple<int,int>>,IEnumerable<Tuple<int,int>>>(missing, overlapping);
        }