从数组中识别和收集连续范围[重复]
Posted
技术标签:
【中文标题】从数组中识别和收集连续范围[重复]【英文标题】:Identifying and collecting consecutive ranges from an array [duplicate] 【发布时间】:2021-10-26 09:31:46 【问题描述】:任务
给定 int[]
数组,例如我必须识别连续范围并构建相应的 String
另一个例子是
注意事项
我可以假设数组已经有序并且范围内没有没有重叠。
此外,数组可能为空。
想法
我知道如何构建一个仅列出所有数字的字符串,例如4, 5, 6, 8, 14, 15, 16
,但我不知道如何识别范围,尤其是如何在它们之间添加~
。
我认为正则表达式在这里可能有用,但我不确定。
【问题讨论】:
"我应该使用正则表达式吗..?" - 不,正则表达式用于查找字符串中的模式,并且您有一个数字数组。所以你需要做的是遍历你的数组,找到所有出现的连续数字,获取第一个和最后一个,将它们添加到字符串中,并在它们之间添加波浪号。 从左到右迭代并建立递增的范围。每次范围结束时,您都会将其结束并结束。 关于“我如何找到连续的数字?”:跟踪可以形成范围的第一个数字以及迭代的当前和最后一个数字。如果 "current" 和 "last" 之间的差 > 1,则 "last" 是上一个范围的结束,而 "current" 是下一个范围的开始。现在只需相应地构建输出并考虑 3 种可能的情况:1)范围的开始和结束数字相同 -> 只打印数字,2)开始和结束有 1 的差异 -> 决定是否打印 @987654325 @ 或start~end
, 3) 打印 start~end
其余部分
【参考方案1】:
试试这个。
static String summary(int[] input)
int length = input.length;
if (length == 0) return "";
StringBuilder sb = new StringBuilder();
new Object()
int start = input[0];
int end = input[0];
String separator = "";
void append()
sb.append(separator).append(start);
if (start != end)
sb.append("~").append(end);
separator = ", ";
void make()
for (int i = 1; i < length; ++i)
int current = input[i];
if (current != end + 1)
append();
start = current;
end = current;
append();
.make();
return sb.toString();
public static void main(String[] args)
System.out.println(summary(new int[] 4, 5, 6, 8, 14, 15, 16));
System.out.println(summary(new int[] 13, 14, 15, 16, 21, 23, 24, 25, 100));
输出:
4~6, 8, 14~16
13~16, 21, 23~25, 100
【讨论】:
请注意,不鼓励仅使用代码回答。解释会很有帮助。 匿名的Object
有点奇怪,一个专门的课程会更好更干净。【参考方案2】:
说明
您可以简单地从左到右进行迭代并在此过程中保持范围。
为此,只需记住范围的开始和每次迭代中的最后一个值。然后,您可以轻松确定范围是刚刚停止还是继续。如果停止,结束该范围并开始下一个。
对于空数组的特殊情况,只需在方法的开头添加一个简单的if
,单独处理即可。
构建范围
public static String buildRanges(int[] values)
if (values.length == 0)
return "";
StringJoiner result = new StringJoiner(", ");
int rangeStart = values[0];
int lastValue = values[0];
// Skip first value to simplify 'lastValue' logic
for (int i = 1; i < values.length; i++)
int value = values[i];
if (value != lastValue + 1)
// Range ended, wrap it up
int rangeEnd = lastValue;
result.add(rangeStart == rangeEnd
? Integer.toString(rangeStart)
: rangeStart + "~" + rangeEnd);
rangeStart = value;
lastValue = value;
// Conclude last range
int rangeEnd = lastValue;
result.add(rangeStart == rangeEnd
? Integer.toString(rangeStart)
: rangeStart + "~" + rangeEnd);
return result.toString();
简化
为了解决代码重复和提高可读性,我建议还引入一个辅助方法rangeToString
:
public static String rangeToString(int rangeStart, int rangeEnd)
return rangeStart == rangeEnd
? Integer.toString(rangeStart)
: rangeStart + "~" + rangeEnd);
然后代码简化为:
public static String buildRanges(int[] values)
if (values.length == 0)
return "";
StringJoiner result = new StringJoiner(", ");
int rangeStart = values[0];
int lastValue = values[0];
// Skip first value to simplify 'lastValue' logic
for (int i = 1; i < values.length; i++)
int value = values[i];
if (value != lastValue + 1)
// Range ended, wrap it up
result.add(rangeToString(rangeStart, lastValue);
rangeStart = value;
lastValue = value;
// Conclude last range
result.add(rangeToString(rangeStart, lastValue);
return result.toString();
面向对象的解决方案
如果您愿意,还可以引入专门的Range
类来解决这个问题。在这种特殊情况下可能有点矫枉过正,但仍然如此。
让我们首先创建一个知道其开始和结束的Range
类。此外,它可以正确地将自身转换为String
,您可以尝试增加范围。
public final class Range
private final int start;
private int end;
public Range(int value)
start = value;
end = value;
public boolean add(int value)
if (value != end + 1)
return false;
end = value;
return true;
@Override
public String toString()
return start == end
? Integer.toString(start)
: start + "~" + end;
现在您可以轻松地在一个简单的循环中使用它:
public static String buildRanges(int[] values)
if (values.length == 0)
return "";
StringJoiner result = new StringJoiner(", ");
Range range = new Range(values[0]);
// Skip first value to simplify logic
for (int i = 1; i < values.length; i++)
int value = values[i];
if (!range.add(value))
// Range ended, wrap it up
result.add(range.toString());
range = new Range(value);
// Conclude last range
result.add(range.toString());
return result.toString();
收集到List<Range>
这种方法的优点是您还可以收集到List<Range> ranges
之类的东西,然后继续处理数据,而不仅仅是转到String
。示例:
public static List<Range> buildRanges(int[] values)
if (values.length == 0)
return List.of();
List<Range> ranges = new ArrayList<>();
Range range = new Range(values[0]);
// Skip first value to simplify logic
for (int i = 1; i < values.length; i++)
int value = values[i];
if (!range.add(value))
// Range ended, wrap it up
ranges.add(range);
range = new Range(value);
// Conclude last range
ranges.add(range);
return ranges;
如果您还向Range
类添加一些getStart()
和getEnd()
方法,则特别有用。
注意事项
请注意,如果数组包含重复项,则该方法可能会表现得很奇怪。您没有指定在这种情况下要做什么,所以我只是假设您的用例不存在重复项。
【讨论】:
如果数组包含重复项,最好将其转换为 Set: ''' new HashSetTreeSet
,但是你做了两次排序的努力,这是不必要的。如果重复是一件事(OP没有指定),在循环期间处理它们并简单地跳过它们可能更容易(数组是有序的,所以跳过它们非常简单)。【参考方案3】:
假设你有以下Interval
类:
public class Interval
public final int min;
public final int max;
public Interval(int min, int max)
this.min = min;
this.max = max;
public Interval(int minmax)
this(minmax, minmax);
public static List<Interval> toIntervals(int... values)
List<Interval> list = new ArrayList<>();
for (int val: values)
addInterval(list, val, val);
return list;
private static void addInterval(List<Interval> list, int min, int max)
int i = 0;
while (i < list.size())
Interval cur = list.get(i);
if (max < cur.min-1)
break;
else if (cur.max >= min-1)
min = Math.min(min, cur.min);
max = Math.max(max, cur.max);
list.remove(i);
else
++i;
list.add(i, new Interval(min, max));
@Override
public String toString()
if (min == max)
return Integer.toString(min);
return min + "~" + max;
您可以将示例中的数字列表转换为区间列表:
List<Interval> list = Interval.toIntervals(4, 5, 6, 8, 14, 15, 16);
或者:
List<Interval> list = Interval.toIntervals(13, 14, 15, 16, 21, 23, 24, 25, 100);
然后打印这样的列表:
System.out.println(list.stream()
.map(Interval::toString)
.collect(Collectors.joining(", ")));
【讨论】:
【参考方案4】:这是一个整数到字符串的转换,而不是 java 中的 cast,这意味着 (TYPE2)OBJECTOFTYPE1
例如 int-to-double (widening) 或 Object -to-String。
一个人必须自己做的范围。
int[] a = 13, 14, 15, 16, 21, 23, 24, 25, 100;
StringBuilder sb = new StringBuilder();
if (a.length > 0)
for (int i = 0; i < a.length; ++i)
if (sb.length() != 0)
sb.append(", ");
sb.append(Integer.toString(a[i]));
int rangeI = i;
while (a[i] != Integer.MAX_VALUE && a[i] + 1 == a[i + 1])
++i;
if (i != rangeI)
sb.append('~').append(Integer.toString(a[i]));
return sb.toString();
您可以使用append(a[i])
,因为有一个重载方法在内部进行转换。为了清楚起见。
逗号分隔很标准。
并且为每个元素检查其增加的追随者的范围。
由于 java 忽略整数溢出,因此检查 MAX_VALUE。
【讨论】:
【参考方案5】:将整数转换为字符串并使用 Java 的 StringBuilder 类附加它们:https://www.geeksforgeeks.org/stringbuilder-class-in-java-with-examples/
StringBuilder str = new StringBuilder();
并使用以下方法附加数字和“~”(波浪号)符号:
str.append(start_number);
str.append('~');
str.append(end_number);
【讨论】:
真正的问题是如何提取范围,而不是如何将 int 转换为字符串。 然后用for循环迭代并累积范围,直到达到它与下一个之间的范围大于1的数字,然后附加StringBuilder并重置新的范围。跨度> 这个答案没有错,但它只关注任务中可能最小和最简单的部分。它只是不完整。 正如之前所说的那样,他似乎在制作字符串本身时遇到了麻烦。经过编辑和澄清,他当然想要范围本身。 我明白了,那完全公平。不幸的是,OP 改变了他们的问题,使您的答案看起来不太好。以上是关于从数组中识别和收集连续范围[重复]的主要内容,如果未能解决你的问题,请参考以下文章