【问题标题】:Java: Convert int[] to smallest representation as rangesJava:将 int[] 转换为范围的最小表示
【发布时间】:2015-09-24 08:13:21
【问题描述】:

给定一组 int 值,如何将系列解析为计数序列符号?

例子:

{1, 2, 3, 4, 5, 9, 13, 14, 15} -> "1-5,9,13-15"
{4, 6, 8, 10, 11, 12, 15, 17}  -> "4,6,8,10-12,15,17"

我正在寻找一种能够产生这些结果的方法。这是我到目前为止所拥有的,但我现在非常难过:

测试代码:

import java.util.Arrays;
public class TestSequencing {
    public static void main(String[] args) {
        int[] numbers1 = {1, 2, 3, 4, 5, 9, 13, 14, 15};
        String numbers1s = "1-5,9,13-15";
        System.out.println(Arrays.toString(numbers1));
        System.out.println("Expected:\t" + numbers1s);
        System.out.println("Produced:\t" + sequenceNums(numbers1) + "\n");

        int[] numbers2 = {3, 5, 6, 9, 12};
        String numbers2s = "3,5-6,9,12";
        System.out.println(Arrays.toString(numbers2));
        System.out.println("Expected:\t" + numbers2s);
        System.out.println("Produced:\t" + sequenceNums(numbers2) + "\n");

        int[] numbers3 = {1, 2, 3, 4, 5, 6, 7};
        String numbers3s = "1-7";
        System.out.println(Arrays.toString(numbers3));
        System.out.println("Expected:\t" + numbers3s);
        System.out.println("Produced:\t" + sequenceNums(numbers3) + "\n");
    }

    public static String sequenceNums(int[] nums) {
        StringBuilder sb = new StringBuilder();
        int rangeStart = nums[0];
        int previous = nums[0];
        int current;
        int expected = previous + 1;

        for (int i = 1 ; i < nums.length ; i++) {
            current = nums[i];
            expected = previous + 1;               
            if (current != expected || i == (nums.length - 1)) {
                if (current == rangeStart) {
                    sb.append(previous + ",");
                } else {
                    sb.append(rangeStart + "-" + previous + ",");
                }                
                rangeStart = current;
            }              
            previous = current;
        }
        if (sb.charAt(sb.length() - 1) == ',') {
            sb.deleteCharAt(sb.length() - 1);
        }
        return sb.toString();
    }
}

输出:

[1, 2, 3, 4, 5, 9, 13, 14, 15]
Expected:   1-5,9,13-15
Produced:   1-5,9-9,13-14

[3, 5, 6, 9, 12]
Expected:   3,5-6,9,12
Produced:   3-3,5-6,9-9

[1, 2, 3, 4, 5, 6, 7]
Expected:   1-7
Produced:   1-6

【问题讨论】:

  • @SotiriosDelimanolis 请对您的 cmets 保持建设性。我的预期结果已明确说明,我提供的示例说明我的代码不会产生我正在寻找的结果。
  • 我在问你为什么它没有达到你想要的效果。你调试过吗?你发现了什么?
  • @SotiriosDelimanolis 我认为问题与缺少数组中的最后一个数字有关。我尝试过使用其他控制流结构的不同想法,但我不知道如何做到这一点。但是,我提供的示例是我最接近解决问题的方法,因此我将其发布在这里,希望来自新思想的新想法。
  • 我发现它是一个更好的编程用例。可能是面试题!
  • @N99x 修复了您的代码。如果有任何案例失败,请告诉我

标签: java arrays parsing range counting


【解决方案1】:

试试这个:

private static void appendRange(StringBuilder sb, int begin, int end) {
    sb.append(",").append(begin);
    if (end != begin)
        sb.append("-").append(end);
}

public static String sequenceNums(int[] nums) {
    StringBuilder sb = new StringBuilder();
    if (nums.length == 0) return sb.toString();
    int begin = nums[0], end = nums[0];
    for (int cur : nums)
        if (cur - end <= 1)
            end = cur;
        else {
            appendRange(sb, begin, end);
            begin = end = cur;
        }
    appendRange(sb, begin, end);
    return sb.substring(1);
}

@Test
public void testSequenceNums() {
    assertEquals("1-5,9,13-15", sequenceNums(new int[] {1, 2, 3, 4, 5, 9, 13, 14, 15}));
    assertEquals("4,6,8,10-12,15,17", sequenceNums(new int[] {4, 6, 8, 10, 11, 12, 15, 17}));
    assertEquals("1-7", sequenceNums(new int[] {1, 2, 3, 4, 5, 6, 7}));
    assertEquals("", sequenceNums(new int[] {}));
}

【讨论】:

    【解决方案2】:

    在 for 循环中你有两个问题:

    1) 第二个if 应该是if (previous == rangeStart) {

    2) 你没有处理循环中的最后一个数字(i == (nums.length - 1))。

    我会使用以下代码来做到这一点:

    public static String sequenceNums(int[] nums) {
        StringBuilder sb = new StringBuilder();
    
        int rangeStart = nums[0];
        int previous = nums[0];
        int current;
        int expected = previous + 1;
        int size = nums.length;
    
        for (int i = 1 ; i < size ; i++) {
            current = nums[i];
            expected = previous + 1;
    
            if (current != expected) {
                addRange(sb, rangeStart, previous);
                rangeStart = current;
            }
    
            previous = current;
        }
        addRange(sb, rangeStart, nums[size - 1]);
    
        return sb.toString();
    }
    
    private void addRange(StringBuilder sb, int from, int to) {
        if (sb.length() > 0) {
            sb.append(",");
        }
        if (from == to) {
            sb.append(from);
        } else {
            sb.append(from + "-" + to);
        }
    }
    

    【讨论】:

      【解决方案3】:

      这是您的固定代码。

      public class TestSequencing {
      
          public static void main(String[] args) {
              int[] numbers1 = {1, 2, 3, 4, 5, 9, 13, 14, 15};
              String numbers1s = "1-5,9,13-15";
              System.out.println(Arrays.toString(numbers1));
              System.out.println("Expected:\t" + numbers1s);
              System.out.println("Produced:\t" + sequenceNums(numbers1) + "\n");
      
              int[] numbers2 = {3, 5, 6, 9, 12};
              String numbers2s = "3,5-6,9,12";
              System.out.println(Arrays.toString(numbers2));
              System.out.println("Expected:\t" + numbers2s);
              System.out.println("Produced:\t" + sequenceNums(numbers2) + "\n");
      
              int[] numbers3 = {1, 2, 3, 4, 5, 6, 7};
              String numbers3s = "1-7";
              System.out.println(Arrays.toString(numbers3));
              System.out.println("Expected:\t" + numbers3s);
              System.out.println("Produced:\t" + sequenceNums(numbers3) + "\n");
          }
      
          public static String sequenceNums(int[] nums) {
              StringBuilder sb = new StringBuilder();
              int rangeStart = nums[0];
              int previous = nums[0];
              int current;
              int expected = previous + 1;
      
              for (int i = 1 ; i < nums.length ; i++) {
                  current = nums[i];
                  expected = previous + 1;               
                  if (current != expected || i == (nums.length - 1)) {
                      if (current == rangeStart) {
                          sb.append(previous + ",");
                      } else {
                          if(rangeStart != previous) {
                              if(i == nums.length - 1)
                                  sb.append(rangeStart + "-" + current);
                              else
                                  sb.append(rangeStart + "-" + previous + ",");
                          } else {
                              if(i == nums.length - 1)
                                  sb.append(rangeStart + "," + current);
                              else
                                  sb.append(rangeStart + ",");
                          }
                      }                
                      rangeStart = current;
                  }              
                  previous = current;
              }
              if (sb.charAt(sb.length() - 1) == ',') {
                  sb.deleteCharAt(sb.length() - 1);
              }
              return sb.toString();
          }
      
      }
      

      问题是,如果当前值与范围起始值不同,则需要检查两种情况

      i) 如果范围以相同的先前值开始。如果是这样,则不需要用范围分隔相同的数字(例如:9-9 没有意义。只有 9 才有意义)。另一种要处理的情况是到达数组末尾。如果到达数组末尾,即使它不在任何范围内,也应该在末尾添加它

      ii) 否则,如果未到达数组末尾,则范围以先前值开始和结束。如果到达数组的末尾,那将是范围的结束值

      【讨论】:

      • 感谢您的解释!我将@saka1029 的答案标记为已接受,因为我喜欢他的代码简洁高效,但您的回答更具解释性和教育意义。
      【解决方案4】:

      我采用以下方法来表示范围内的整数数组。

      注意:数字应预先按升序排序。

      我们将有两个变量 startcurrent,我们将在迭代中使用它们来识别范围。 index 将是数组的当前索引。

      一旦找到一个范围,我们将继续将其推送到 StringBuilder 中。

      这是代码:

      // We will take this set of integers 
      int[] temp = new int[] { 0, 1, 4, 5, 8, 9, 11, 12, 13 };
      
      // Helper variables
      Integer start = null, current = null;
      
      // The found range(s) will be stored in this variable.
      StringBuilder rangeBuilder = new StringBuilder();
      
      // The current index of the array in iteration
      int index = 0;
      
      do {
          // During the first iteration both start & current will be null. So setting the current index value to them.
          if (start == null) {
              start = current = temp[index];
          } else {
              // Checking if the index value is the next number of current.
              if (temp[index] == (current + 1)) {
                  current = temp[index];
              } else {
                  if (start == current) {
                      rangeBuilder.append(start + ",");
                  } else {
                      rangeBuilder.append(start + "-" + current + ",");
      
                  }
                  start = current = temp[index];
              }
      
          }
      
          // Checking if we have reached the end of the array.
          if (index + 1 == temp.length) {
              if (start == current) {
                  rangeBuilder.append(start);
              } else {
                  rangeBuilder.append(start + "-" + current);
              }
          }
      
      } while (index++ < temp.length - 1);
      
      // Printing the range.
      System.out.println("Range: " + rangeBuilder.toString());
      

      说明:

      我们以这个例子为例:

      { 0, 1, 4, 5, 8, 9, 11, 12, 13 }

      注意:我们将代表 start -> s & current -> c

      迭代 1:

      条目: start = null,current = null,rangeBuilder = ""

      sc
      0,  1,  4,  5,  8,  9,  11,  12, 13
      ^
      

      索引值是当前的下一个数字吗?没有

      索引是否到达最后一个元素?没有

      退出: start = 0, current = 0, rangeBuilder = ""

      迭代 2:

      条目: start = 0,current = 0,rangeBuilder = ""

      s   c
      0,  1,  4,  5,  8,  9,  11,  12, 13
          ^
      

      索引值是当前的下一个数字吗?是的。所以我们将 c0 -> 1 移动。

      索引是否到达最后一个元素?没有

      退出: start = 0,current = 1,rangeBuilder = ""

      迭代 3:

      条目: start = 0,current = 1,rangeBuilder = ""

              sc
      0,  1,  4,  5,  8,  9,  11,  12, 13
              ^
      

      索引值是当前的下一个数字吗?不,所以我们将 sc 推入字符串生成器 (rangeBuilder)。在推送时,我们检查 sc 是否相同以避免重复。然后我们将 s & c 移动到索引值。

      索引是否到达最后一个元素?没有

      退出: start = 4, current = 4, rangeBuilder = "0-1,"

      迭代 4:

      条目: start = 4,current = 4,rangeBuilder = "0-1,"

              s   c
      0,  1,  4,  5,  8,  9,  11,  12, 13
                  ^
      

      索引值是当前的下一个数字吗?是的。所以我们将 c4 -> 5 移动。

      索引是否到达最后一个元素?没有

      退出: start = 4, current = 5, rangeBuilder = "0-1,"

      迭代 5:

      条目: start = 4, current = 5, rangeBuilder = "0-1,"

                      sc
      0,  1,  4,  5,  8,  9,  11,  12, 13
                      ^
      

      索引值是当前的下一个数字吗?不,所以我们将 sc 推入字符串生成器 (rangeBuilder)。在推送时,我们检查 sc 是否相同以避免重复。然后我们将 s & c 移动到索引值。

      索引是否到达最后一个元素?没有

      退出: start = 8, current = 8, rangeBuilder = "0-1,4-5,"

      迭代 6:

      条目: start = 8, current = 8, rangeBuilder = "0-1,4-5,"

                      s   c
      0,  1,  4,  5,  8,  9,  11,  12, 13
                          ^
      

      索引值是当前的下一个数字吗?是的。所以我们将 c8 -> 9 移动。

      索引是否到达最后一个元素?没有

      退出: start = 8, current = 9, rangeBuilder = "0-1,4-5,"

      迭代 7:

      条目: start = 8,current = 9,rangeBuilder = "0-1,4-5,"

                              sc
      0,  1,  4,  5,  8,  9,  11,  12, 13
                              ^
      

      索引值是当前的下一个数字吗?不,所以我们将 sc 推入字符串生成器 (rangeBuilder)。在推送时,我们检查 sc 是否相同以避免重复。然后我们将 s & c 移动到索引值。

      索引是否到达最后一个元素?没有

      退出: start = 11, current = 11, rangeBuilder = "0-1,4-5,8-9,"

      迭代 8:

      条目: start = 11,current = 11,rangeBuilder = "0-1,4-5,8-9,"

                              s    c
      0,  1,  4,  5,  8,  9,  11,  12, 13
                                   ^
      

      索引值是当前的下一个数字吗?是的。所以我们将 c11 -> 12 移动。

      索引是否到达最后一个元素?没有

      退出: start = 11, current = 12, rangeBuilder = "0-1,4-5,8-9,"

      迭代 9:

      条目: start = 11,current = 12,rangeBuilder = "0-1,4-5,8-9,"

                              s        c
      0,  1,  4,  5,  8,  9,  11,  12, 13
                                       ^
      

      索引值是当前的下一个数字吗?是的。所以我们将 c12 -> 13 移动。

      索引是否到达最后一个元素?是的。所以我们将 sc 推入 String Builder (rangeBuilder)。在推送时,我们检查 sc 是否相同以避免重复。

      迭代结束。字符串生成器 (rangerBuilder) 将具有以下值:0-1,4-5,8-9,11-13

      请随时改进此代码:)

      【讨论】:

        【解决方案5】:

        我能够通过引入布尔标志并重新设计测试连续数字的方法来解决您的问题。如果当前和下一个数字是连续的,则触发inRangeFlag 进行下一次迭代。请参阅下面的代码 cmets 以获得进一步的细分:

           public static String sequenceNums(int[] nums) {
        
            StringBuilder sb = new StringBuilder();
            int current;
            int next;
            boolean inRangeFlag = false;
        
            for (int i = 0; i < nums.length; i++) {
        
                current = nums[i];
        
                // TRUE: if element is not last element, because last number is
                // always appended.
                if (i < nums.length - 1) {
        
                    next = nums[i + 1];
        
                    // TRUE: if current element and next are consecutive
                    if (next - current == 1) {
        
                        // If rangeflag is false, the current number is the start
                        // of a range. Append the number with hyphen.
                        if (!inRangeFlag) {
                            sb.append(current + "-");
                        }
        
                        // Trigger inRange Flag for next iteration.
                        inRangeFlag = true;
        
                    } else {
                        sb.append(current + ",");
                        inRangeFlag = false; // Turn flag false because not inRange.
                    }
                } else {
                    sb.append(current);
                }
            }
            return sb.toString();
        }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2015-11-08
          • 2021-11-08
          • 2023-04-09
          • 1970-01-01
          • 1970-01-01
          • 2013-10-16
          • 2016-06-15
          • 2012-07-13
          相关资源
          最近更新 更多