【问题标题】:Detecting consecutive integers and collapse to string检测连续整数并折叠成字符串
【发布时间】:2020-12-14 23:57:33
【问题描述】:

免责声明:A very similar question was already asked in a Python context here. 这是关于 C# 的。

我有一个包含整数的枚举,例如:

[1, 2, 3, 4, 7, 8, 10, 11, 12, 13, 14]

我想获得一个字符串,输出连续整数的范围:

1-4, 7-8, 10-14

我想出了:

public static void Main()
{
    System.Diagnostics.Debug.WriteLine(FindConsecutiveNumbers(new int[] { 1,2, 7,8,9, 12, 15, 20,21 }));
}

private static string FindConsecutiveNumbers(IEnumerable<int> numbers)
{
    var sb = new StringBuilder();
    int? start = null;
    int? lastNumber = null;
    const string s = ", ";
    const string c = "-";

    var numbersPlusIntMax = numbers.ToList();
    numbersPlusIntMax.Add(int.MaxValue);
    foreach (var number in numbersPlusIntMax)
    {
        var isConsecutive = lastNumber != null && lastNumber + 1 == number;
        if (!isConsecutive)
        {
            if (start != null)
            {
                if (sb.Length > 0) { sb.Append(s); }
                if (start == lastNumber)
                {
                    sb.Append(start); ;
                }
                else
                {
                    sb.Append(start + c + lastNumber); ;
                }
            }

            start = number;
        }
                
        lastNumber = number;
    }

    return sb.ToString();
}

此算法适用于有序输入。是否有内置/LINQ/更短的 C# 方式来执行此操作?

【问题讨论】:

    标签: c# list algorithm


    【解决方案1】:
    int[] numbers = { 1, 2, 3, 4, 7, 8, 10, 11, 12, 13, 14 };
    
    return string.Join(", ",
        numbers
            .Select((n, i) => new { value = n, group = n - i })
            .GroupBy(o => o.group)
            .Select(g => g.First().value + "-" + g.Last().value)
    );
    

    【讨论】:

    • .Select(g =&gt; g.First().value == g.Last().value ? $"{g.First().value}" : $"{g.First().value}-{g.Last().value}") 以坚持所需的格式
    【解决方案2】:

    我建议分解:让我们将初始例程拆分为逻辑

    private static IEnumerable<(int left, int right)> Consecutive(IEnumerable<int> numbers) {
      int start = -1;
      int stop = -2;  
    
      foreach (var item in numbers) // numbers.OrderBy(x => x) to sort numbers
        if (item == stop + 1)
          stop = item;
        else {
          if (stop >= start)
            yield return (start, stop);
    
          start = item;
          stop = item;
        }  
    
      if (stop >= start)
        yield return (start, stop);  
    }
    

    表示

    private static string FindConsecutiveNumbers(IEnumerable<int> numbers) =>
      string.Join(", ", Consecutive(numbers)
        .Select(item => item.left == item.right 
           ? $"{item.left}" 
           : $"{item.left}-{item.right}"));
    

    然后一切照旧:

    public static void Main()
    {
        // 1-2, 7-9, 12, 15, 20-21
        System.Diagnostics.Debug.WriteLine(FindConsecutiveNumbers(new int[] { 
          1, 2, 
          7, 8, 9, 
          12, 
          15, 
          20, 21 }
        ));
    }
    

    【讨论】:

    【解决方案3】:

    如果我们可以在 List 而不是枚举中获取数字,那么我们不需要访问所有数字。前面的答案的时间复杂度为 O(n),其中 n 是数组的大小。而如果我们有一个列表,这个问题可以在 O(klogn) 中解决,其中 k 是组数。

    基本上你可以使用稍微修改的二分搜索来找到一个组的终点,这可以在 O(logn) 中完成,然后从 end+1 开始,你可以为下一个组做同样的事情。

    【讨论】:

    • 二分查找。也是个好主意。但是,如果我正确理解了这种方法,那么只有在少数群体的情况下,这才是优势。
    • 没错。如果预计每个组都只有很少的元素(这意味着有很多组),那么使用这种方法是没有意义的。
    • @JackMiller,我们还可以修改二分搜索,在最坏的情况下,在 k = n 的情况下它将是 O(n) 而不是 O(nlogn)。因此复杂度会比 O(klogn) 和 O(n) 更好。
    猜你喜欢
    • 1970-01-01
    • 2015-03-16
    • 2018-10-28
    • 1970-01-01
    • 1970-01-01
    • 2011-09-22
    • 2010-10-19
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多