将 1,2,3,4,5,6,8,10,11 显示为 1-6,8,10-11

Posted

技术标签:

【中文标题】将 1,2,3,4,5,6,8,10,11 显示为 1-6,8,10-11【英文标题】:Display 1,2,3,4,5,6,8,10,11 as 1-6,8,10-11 【发布时间】:2013-02-03 20:36:20 【问题描述】:

我有这个序列1,2,3,4,5,6,8,10,11

预期输出为 1-6,8,10-11

这个问题是关于以易于阅读的形式格式化序列

我尝试使用 c# 并使用了很多 if & else。

面试官说,有一些简单的算法可以做到这一点。

我不知道如何实现这个非常简单。

对于 1,2,3 我也显示了 1-3。他们说错了!。

此逻辑中是否涉及任何设计模式(解释器)?

【问题讨论】:

他们有没有说为什么从1,2,3 转换为1-3 是错误的?我看不出这个案子有什么问题 java display input in range 的可能重复项 是的,我只是想知道1-3 是否错误,因为您应该输出1-4,或者它是否错误,因为他们只想替换一行中的 4+ 个元素,不是连续 3 个元素。 我喜欢这个问题有九个代码答案,而没有解释的答案。 (是的,这是一个非常简单的算法;但仍然如此。) @KonradRudolph:如果您认为有必要,您可以随意添加。 【参考方案1】:

这是一种方法:

        int[] numbers =  1, 2, 3, 4, 5, 6, 8, 10, 11 ;

        int start, end;
        for (int i = 0; i < numbers.Length; i++)
        
            start = numbers[i];

            while (i < numbers.Length - 1 && numbers[i] + 1 == numbers[i + 1])
                i++;

            end = numbers[i];

            if(start == end)
                Console.WriteLine(start);
            else
                Console.WriteLine(start + " - " + end);
        

这将显示随着范围递增的后续数字。不是线性增加的数字不会写成范围的一部分。

这是第一种方法的另一个版本,它利用相同的for 循环来迭代范围:

        int temp = numbers[0], start, end;
        for (int i = 0; i < numbers.Length; i++)
        
            start = temp;

            if (i < numbers.Length - 1 )
                // if subsequent numbers are incremental loop further
                if (numbers[i] + 1 == numbers[i + 1])
                    continue;
                // if they are not, number at index i + 1 is a new 'start' for the next iteration
                else
                    temp = numbers[i + 1];

            end = numbers[i];

            if (start == end)
                Console.WriteLine(start);
            else
                Console.WriteLine(start + " - " + end);
        

【讨论】:

这个逻辑对我来说似乎很简单:) 我同意,逻辑和实现都很简单:) @IvanG 可以反过来吗?如果用户输入为 10,1-4,5-8, 9,则输出应为 1,2,3,4,5,6,7,8,9,10【参考方案2】:

C# 中的一个简单实现可能如下所示:

public string Format(IEnumerable<int> input)

    var result = string.Empty;

    var previous = -1;
    var start = -1;
    var first = true;

    foreach(var i in input)
    
        if(start == -1)
            start = i;
        else if(previous + 1 != i)
        
            result += FormatRange(start, previous, first);
            first = false;
            start = i;
        

        previous = i;
    

    if(start != -1)
        result += FormatRange(start, previous, first);

    return result;


public string FormatRange(int start, int end, bool isFirst)

    var result = string.Empty;
    if(!isFirst)
        result += ", ";
    if(start == end)
        result += start;
    else
        result += string.Format("0-1", start, end);
    return result;

这也将为输入1,2,3 输出1-3,这是完全有效的。如果没有说明输出应该是什么,则无法回答该部分。

【讨论】:

很好的答案,但我认为重点(至少作为面试问题)是找到一个简单的算法,而不是通过使用 API 来“作弊”。 @Dukeling:识别范围使用简单算法完成的。 @Dukeling:但我还是将其更改为不使用 LINQ 进行输出。【参考方案3】:

可能不是面试问题的合适答案,但使用 LINQ 是解决此问题的另一种方法。

int[] numbers =  1, 2, 3, 4, 5, 6, 8, 10, 11 ;
var remains = numbers.AsEnumerable();

while (remains.Any())

    int first = remains.First();
    int last = remains.TakeWhile((x, i) => x - first == i).Last();
    remains = remains.Skip(last - first + 1);
    Console.Write(first + (first == last ? "" : "-" + last) + (remains.Any() ? "," : Environment.NewLine));

【讨论】:

这是这里最简洁的版本。 请注意,这是在迭代源序列 很多 次,这不适用于任意可枚举,即使它适用于数组。【参考方案4】:

以下分组连续整数,并为每个组输出一个字符串。但是,它还允许您指定要连字的组的最小长度;少一点只会给你个人数字。因此,如果您只想连接 4 个或更多的组,则可以传入 4;如果你想用连字符对,你可以传入 2。(我自己也想用 3,但我不知道他们想要什么。)

它也不会保留任何数字集合,因为您不需要。

方法:

static IEnumerable<string> Group(IEnumerable<int> input, int minLength)

    int currentStart = int.MinValue;
    int currentLength = 0;
    foreach (int c in input)
    
        if (currentLength > 0)
            if (currentStart + currentLength == c)
                currentLength++;
            else
            
                if (currentLength >= minLength)
                    yield return string.Format("0-1",
                        currentStart, currentStart + currentLength - 1);
                else
                    for (int i = currentStart; i < currentStart + currentLength; i++)
                        yield return i.ToString();
                currentStart = c;
                currentLength = 1;
            
        else
        
            currentStart = c;
            currentLength = 1;
        
    
    if (currentLength >= minLength)
        yield return string.Format("0-1",
            currentStart, currentStart + currentLength + 1);
    else
        for (int i = currentStart; i < currentStart + currentLength; i++)
            yield return i.ToString();

用法:

int minCount = 3;
int[] input = new[]  1, 2, 3, 4, 5, 6, 8, 10, 11 ;
Console.WriteLine(String.Join(",", Group(input, minCount)));

【讨论】:

【参考方案5】:

Java 代码:

int[] arr = 1,2,3,4,5,6,8,10,11;
int start = arr[0], last = arr[0];
String output = "";

for (int i = 1; i <= arr.length; i++)

  if (i == arr.length || arr[i] != last+1)
  
    if (output.length() != 0)
      output += ",";
    if (start == last)
      output += start;
    else
      output += start + "-" + last;
    if (i != arr.length)
      start = last = arr[i];
  
  else
     last = arr[i];


System.out.println(output);

【讨论】:

【参考方案6】:

这是我最好的尝试。不聪明,但足够简单,足以满足我相信的要求。我仍然很困惑为什么“1-3”是错误的......

    var numbers = new int[]  1, 2, 3, 4, 5, 6, 8, 10, 11, 12 ;

    var groups = new Dictionary<int, int>();
    groups.Add(numbers.First(), numbers.First());

    foreach (var num in numbers.Skip(1))
    
        var grp = groups.Last();
        if (grp.Value + 1 == num)
        
            groups[grp.Key] = num;
        
        else
        
            groups.Add(num, num);
        
    

    var output = string.Join(",", groups.Select(grp => (grp.Key == grp.Value) ? grp.Value.ToString() : grp.Key.ToString() + "-" + grp.Value.ToString()));

注意:当然,使用字典和 linq 等是完全没有必要的(对于需要算法的答案来说太具体了),但我认为它很好地突出了问题的分组方面

【讨论】:

【参考方案7】:

这不是有效的 C# 代码,只是为了展示想法。

从 Min 到 Max 对列表进行排序,然后执行以下操作:

For i = Min to Max

  if i < MaxFound
    continue;

  int step = 1;
  Output = i;
  while Found(i + Step)
  
     Step++;
     MaxFound = i + Step;
  
  if i < MaxFound 
    Output = (i + "-" + MaxFound);

  Output += ", ";

【讨论】:

【参考方案8】:

这是一种方法:

public static void main(String[] args) 
    print(1, 2, 3, 4, 5, 7, 9, 10, 12);


public static void print(int ... nums) 
    System.out.print(nums[0]);
    int idx = 1;

    for(int i = 1; i < nums.length; i++, idx++) 
        if(nums[i] - nums[i - 1] != 1) 
            if(idx > 1) 
                System.out.print(" - " + nums[i - 1]);
            
            System.out.print(", " + nums[i]);
            idx = 0;
        
    

    if(idx > 1)
        System.out.println(" - " + nums[nums.length - 1]);

【讨论】:

我没有看到格式中的第一个数字。它显示 -5 而不是 1-5 @BadDeveloper 第一行代码打印第一个元素。可能你错过了!【参考方案9】:

这是一个 Haskell 版本:

import Data.List

parseRange [] = ""
parseRange n = 
  let range = takeWhile (\x -> isInfixOf [x,x+1] n) n
  in if not (null range)
        then show (head range) ++ "-" ++ show (last range + 1) 
             ++ (if length (tail n) > 1 then "," else "") 
             ++ parseRange (drop (length range + 1) n) 
        else show (head n) ++ (if null (tail n) then "" else ",") 
             ++ parseRange (drop 1 n)

输出:

*Main> parseRange [1,2,3,4,5,6,8,10,11]
"1-6,8,10-11"

【讨论】:

【参考方案10】:

还有一种在 F# 中使用 fold 的方法 - 只是为了好玩。

let parseRange numbers = 
  numbers 
  |> Seq.fold 
    (fun list n -> 
      match list with
      |(a,b) :: tail when b+1 = n -> (a, n) :: tail
      |_ -> (n,n) :: list) []
  |> List.rev 
  |> Seq.map (fun (a,b) -> if a = b then sprintf "%i" a else sprintf "%i-%i" a b)
  |> String.concat ","

【讨论】:

以上是关于将 1,2,3,4,5,6,8,10,11 显示为 1-6,8,10-11的主要内容,如果未能解决你的问题,请参考以下文章

将一个 JS 数组拆分为 N 个数组

为啥 torch.FloatTensor([[[0,1,2],[3,4,5]],[[6,7,8],[9,10,11]]]) 的大小是 [2,2, 3]? [关闭]

已知如下数组: var arr = [ [1, 2, 2], [3, 4, 5, 5], [6, 7, 8, 9, [11, 12, [12, 13, [14] ] ] ], 10]; 编写一个程

之字形打印矩阵

python: 输出1 3 4 10 2 5 9 11 6 8 12 15 7 13 14 16

软件测试作业4