Java:将 int[] 转换为范围的最小表示

Java: Convert int[] to smallest representation as ranges

给定一组 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

在 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);
    }
}

试试这个:

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[] {}));
}

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

   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();
}

这是您的固定密码。

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) 否则,如果未到达数组末尾,则范围以前一个值开始和结束。如果到达数组末尾,那将是范围的结束值

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

注意:号码要预先按升序排列。

我们将有两个变量 start & current,我们将在迭代中使用它们来识别范围。 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
^

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

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

退出:开始=0,当前=0,rangeBuilder=“”

迭代 2:

条目: 开始 = 0,当前 = 0,rangeBuilder = ""

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

索引值是当前的下一个数吗?是的。所以我们从 0 -> 1.

移动 c

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

退出:开始=0,当前=1,rangeBuilder=“”

第 3 次迭代:

条目: 开始 = 0,当前 = 1,rangeBuilder = ""

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

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

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

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

第 4 次迭代:

条目: 开始 = 4,当前 = 4,rangeBuilder =“0-1,”

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

索引值是当前的下一个数吗?是的。所以我们从 4 -> 5.

移动 c

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

退出:开始=4,当前=5,rangeBuilder=“0-1,”

第 5 次迭代:

条目: 开始 = 4,当前 = 5,rangeBuilder =“0-1,”

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

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

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

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

第 6 次迭代:

条目: 开始 = 8,当前 = 8,rangeBuilder = "0-1,4-5,"

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

索引值是当前的下一个数吗?是的。所以我们从 8 -> 9.

移动 c

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

退出:开始=8,当前=9,rangeBuilder="0-1,4-5,"

迭代 7:

条目: 开始 = 8,当前 = 9,rangeBuilder = "0-1,4-5,"

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

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

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

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

迭代-8:

条目: 开始 = 11,当前 = 11,rangeBuilder = "0-1,4-5,8-9,"

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

索引值是当前的下一个数字吗?是的。所以我们从 11 -> 12.

移动 c

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

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

第 9 次迭代:

条目: 开始 = 11,当前 = 12,rangeBuilder = "0-1,4-5,8-9,"

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

索引值是当前的下一个数吗?是的。所以我们从 12 -> 13.

移动 c

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

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

随时改进此代码:)