给定一个整数数组,迭代它并找出它所涵盖的所有范围的最简单方法是什么?例如,对于一个数组,例如:
$numbers = array(1,3,4,5,6,8,11,12,14,15,16);
范围将是:
1,3-6,8,11-12,14-16
给定一个整数数组,迭代它并找出它所涵盖的所有范围的最简单方法是什么?例如,对于一个数组,例如:
$numbers = array(1,3,4,5,6,8,11,12,14,15,16);
范围将是:
1,3-6,8,11-12,14-16
如果数组按升序排序,那么问题就简单了。定义一个Range
结构或类,它有一个开始和一个结束。然后遍历数组。如果当前元素比前一个元素多一个,则更新Range.end
,否则创建一个新范围,该元素为Range.begin
。将范围存储到动态数组或链表中。或者只是在你去的时候把它们打印出来。
如果数组可能没有排序,那么先排序。
这是 C# 3.0'y 的做法:
兴趣点:
-
class Demo
{
private class Range
{
public int Begin { get; set; }
public int End { get; set; }
public override string ToString()
{
if (Begin == End)
return string.Format("{0}", Begin);
else
return string.Format("{0}-{1}", Begin, End);
}
}
static void Main(string[] args)
{
var list = new List<int> { 1, 3, 4, 5, 6, 8, 11, 12, 14, 15, 16 };
// list.Sort();
var ranges = GetRangesForSortedList(list);
PrintRange(ranges);
Console.Read();
}
private static void PrintRange(IEnumerable<Range> ranges)
{
if (ranges.Count() == 0)
return;
Console.Write("[{0}", ranges.First());
foreach (Range range in ranges.Skip(1))
{
Console.Write(", {0}", range);
}
Console.WriteLine("]");
}
private static IEnumerable<Range> GetRangesForSortedList(IList<int> sortedList)
{
if (sortedList.Count < 1)
yield break;
int firstItem = sortedList.First();
Range currentRange = new Range { Begin = firstItem, End = firstItem };
foreach (int item in sortedList.Skip(1))
{
if (item == currentRange.End + 1)
{
currentRange.End = item;
}
else
{
yield return currentRange;
currentRange = new Range { Begin = item, End = item };
}
}
yield return currentRange;
}
}
干杯,大卫
这是一个python实现,应该很容易理解
numbers = [1,3,4,5,6,8,11,12,14,15,16];
def is_predecessor(i1, i2):
if i1 == i2 - 1:
return True;
else:
return False;
def make_range(i1, i2):
if i1 == i2:
return str(i1);
else:
return str(i1) + "-" + str(i2);
previous_element = None;
current_start_element = None;
for number in numbers:
if not is_predecessor(previous_element, number):
if current_start_element is not None:
print make_range(current_start_element, previous_element);
current_start_element = number;
previous_element = number;
# handle last pair
if current_start_element is not None:
print make_range(current_start_element, previous_element);
这输出:
1
3-6
8
11-12
14-16
我知道,我知道,它不是一种算法,但我发现在没有缩进问题的情况下实际解释它比仅仅为它实现一个解决方案更难。
第一个:排序第二个:标记化然后:打印第一个值并循环后续的值。如果“当前”值等于最后一个值 +1,则跳过它。否则,如果您跳过了值打印破折号和值,否则打印逗号并重复。
应该这样做。除非你想让我帮你写作业?:)
如果数组已排序,如您的示例所示,我将定义包含最小值和最大值的存储桶。
初始化:创建一个最小值和最大值等于第一个值的存储桶。
循环:将每个值与当前桶的最大值进行比较。如果它等于或大于当前最大值 1,则更新最大值。如果它比最大值大 1 多,则将存储桶保存到存储桶列表并启动一个新存储桶。
最后,您将有一组桶,每个桶都有一个最小值和一个最大值。如果最小值与最大值相同,则打印一个数字(即:在您的示例中,第一个存储桶的最小值和最大值为 1)。如果它们不同,则打印为范围。
lisp 中的示例实现:
CL-USER> (defun print-ranges (integer-list)
(let ((sorted-list (sort integer-list #'<)))
(loop with buckets = ()
with current-bucket
for int in sorted-list
initially (setf current-bucket (cons (first sorted-list) (first sorted-list)))
do (cond ((= int (cdr current-bucket))) ; do nothing, this number is already in range
((= (1- int) (cdr current-bucket)) ; number is 1 higher--update bucket's max
(setf (cdr current-bucket) int))
(t
(push current-bucket buckets)
(setf current-bucket (cons int int)))) ; set up a new bucket
finally (push current-bucket buckets)
(loop for firstp = t then nil
for bucket in (nreverse buckets) do
(cond ((= (car bucket) (cdr bucket))
(format t "~:[,~;~]~D" firstp (car bucket)))
(t
(format t "~:[,~;~]~D-~D" firstp (car bucket) (cdr bucket))))))))
PRINT-RANGES
CL-USER> (print-ranges (list 1 3 4 5 6 8 11 12 14 15 16))
1,3-6,8,11-12,14-16
NIL
CL-USER>
基本上,您最终会得到一个事物列表,其中每个事物都有(最低桶,最高桶)。这些是你的范围。
如果列表尚未排序,请先排序。
它类似于Python 的版本。
void ranges(int n; int a[n], int n)
{
qsort(a, n, sizeof(*a), intcmp);
for (int i = 0; i < n; ++i) {
const int start = i;
while(i < n-1 and a[i] >= a[i+1]-1)
++i;
printf("%d", a[start]);
if (a[start] != a[i])
printf("-%d", a[i]);
if (i < n-1)
printf(",");
}
printf("\n");
}
例子:
/**
* $ gcc -std=c99 -Wall ranges.c -o ranges && ./ranges
*/
#include <iso646.h> // and
#include <stdio.h>
#include <stdlib.h>
#define T(args...) \
{ \
int array[] = {args}; \
ranges(array, sizeof(array) / sizeof(*array)); \
}
int intcmp(const void* a, const void* b)
{
const int *ai = a;
const int *bi = b;
if (*ai < *bi)
return -1;
else if (*ai > *bi)
return 1;
else
return 0;
}
int main(void)
{
T(1,3,4,5,6,8,11,12,14,15,16);
T();
T(1);
T(1, 2);
T(3, 1);
T(1, 3, 4);
T(1, 2, 4);
T(1, 1, 2, 4);
T(1, 2, 2, 4);
T(1, 2, 2, 3, 5, 5);
}
输出:
1,3-6,8,11-12,14-16
1
1-2
1,3
1,3-4
1-2,4
1-2,4
1-2,4
1-3,5
假设列表是有序的,您可以从最后开始并继续减去下一个。虽然差值为 1,但您在一个范围内。如果不是,您将开始一个新的范围。
IE
16-15 = 1
15-14 = 1
14-12 = 2,范围是 16-14 - 开始一个新的范围。
startRange = array[0];
for(i=0;i<array.length;i++)
{
if (array[i + 1] - array[i] > 1)
{
endRange = array[i];
pushRangeOntoArray(startRange,endRange);
i++;
startRange = array[i]
// need to check for end of array here
}
}
这是我的 Perl 解决方案。可能更干净,更快,但它显示了它是如何工作的:
# Just in case it's not sorted...
my @list = sort { $a <=> $b } ( 1, 3, 4, 5, 6, 8, 11, 12, 14, 15, 16 );
my $range = [ $list[0] ];
for(@list[1 .. $#list]) {
if($_ == $range->[-1] + 1) {
push @$range, $_;
}
else {
print $#$range ? $range->[0] . '-' . $range->[-1] : $range->[0], "\n";
$range = [ $_ ];
}
}
我在 Java 1.5 中的解决方案是:
public static List<String> getRanges(int... in) {
List<String> result = new ArrayList<String>();
int last = -1;
for (int i : in) {
if (i != (last + 1)) {
if (!result.isEmpty()) {
addRange(result, last);
}
result.add(String.valueOf(i));
}
last = i;
}
addRange(result, last);
return result;
}
private static void addRange(List<String> result, int last) {
int lastPosition = result.size() - 1;
String lastResult = result.get(lastPosition);
if (!lastResult.equals(String.valueOf(last))) {
result.set(lastPosition, lastResult + "-" + last);
}
}
public static void main(String[] args) {
List<String> ranges = getRanges(1, 3, 4, 5, 6, 8, 11, 12, 14, 15, 16);
System.out.println(ranges);
}
输出:
[1, 3-6, 8, 11-12, 14-16]
格瑞兹,GHad
我相信在 1.5 版本中引入 Subversion 的 mergeinfo 属性的格式与您要求的格式相同,因此您可以查看 Subversion 的源代码以了解它们是如何做到的。如果它与已在此处发布的其他建议有任何不同,我会感到惊讶。
我将假设数组 X() 是预先排序的(如果不是,则预先对数组进行排序)。
对于 X() 的每个元素作为 $element (以 $i 作为当前数组位置) 将 $element 添加到数组 Y() 的末尾 如果 (X($i) + 1 小于 X($i + 1)) AND ($i + 1 不大于 sizeof(X())) 则 将 Y(1)."-".Y(sizeof(Y())) 附加到 Z() 的末尾 未设置 Y() 万一 下一个 如果 Y() 中有任何内容,则附加到 Z() 的末尾
好吧,我就是这样做的。
创建一个简单的范围类型,其中包含范围值的开始/结束。添加一个只接受一个值并设置 start = end = value 的构造函数。维护一堆范围对象,按照自己的方式处理数组的排序副本,扩展顶部范围或根据需要添加新范围。更具体地说,当数组中的值为 1 + 堆栈中范围对象的结束值时,增加该范围的结束值,如果不是,则推入一个新范围(其中 start = end = value at数组中的索引)到堆栈上。
module Main where
ranges :: [Int] -> [[Int]]
ranges [] = []
ranges list@(x:xs) = let adj = adjacent list in
let len = length adj in
if length adj == 1
then [[x]] ++ ranges xs
else [[x,(last adj)]] ++ ranges (drop ((length adj) - 1) xs)
where adjacent [] = []
adjacent (x:xs) = if (xs /= []) && (x + 1) == head xs
then [x] ++ adjacent (xs)
else [x]
main = do putStrLn $ show $ ranges [1,2,3,4,5,6,8,11,12,14,15,16]
-- Output: [[1,6],[8],[11,12],[14,16]]
这是我在 Haskell 中最好的镜头。
sub to_ranges( Int *@data ){
my @ranges;
OUTER: for @data -> $item {
for @ranges -> $range {
# short circuit if the $item is in a range
next OUTER if $range[0] <= $item <= $range[1];
given( $item ){
when( $range[0]-1 ){ $range[0] = $item }
when( $range[1]+1 ){ $range[1] = $item }
}
}
push @ranges, [$item,$item];
}
return @ranges;
}
此版本还处理重复和未排序的序列。
from __future__ import print_function
def ranges(a):
a.sort()
i = 0
while i < len(a):
start = i
while i < len(a)-1 and a[i] >= a[i+1]-1:
i += 1
print(a[start] if a[start] == a[i] else "%d-%d" % (a[start], a[i]),
end="," if i < len(a)-1 else "\n")
i += 1
例子:
import random
r = range(10)
random.shuffle(r)
ranges(r)
ranges([1,3,4,5,6,8,11,12,14,15,16]);
ranges([])
ranges([1])
ranges([1, 2])
ranges([1, 3])
ranges([1, 3, 4])
ranges([1, 2, 4])
ranges([1, 1, 2, 4])
ranges([1, 2, 2, 4])
ranges([1, 2, 2, 3, 5, 5])
输出:
0-9
1,3-6,8,11-12,14-16
1
1-2
1,3
1,3-4
1-2,4
1-2,4
1-2,4
1-3,5