Given an array of integers, what is the simplest way to iterate over it and figure out all the ranges it covers? for example, for an array such as:
$numbers = ar
My solution in Java 1.5 would be:
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);
}
which outputs:
[1, 3-6, 8, 11-12, 14-16]
Greetz, GHad
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]]
Here's my best shot in Haskell.
Create a simple range type which contains start / end of range values. Add a constructor which takes only one value and sets start = end = value. Maintain a stack of range objects, work your way through a sorted copy of the array, extend the top range or add a new range as appropriate. More specifically, when the value in the array is 1 + the end value for the range object on the to of the stack, increment the end value for that range, when it's not, push a new range (with start = end = value at index in array) onto the stack.
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
}
}
Assuming the list is ordered you could start at the end and keep subtracting the next one down. While the difference is 1, you're in a range. When it's not, you start a new range.
i.e
16-15 = 1
15-14 = 1
14-12 = 2, the range is 16-14 - start a new range.
If the array is sorted in ascending order, then the problem is easy. Define a Range
structure or class, which has a beginning and an end. Then go through the array. If the current element is one more than the previous, update Range.end
, otherwise create a new range with this element as Range.begin
. Store the ranges to a dynamic array or a linked list. Or just print them out as you go.
If the array may not be sorted, then sort it first.