How would you display an array of integers as a set of ranges? (algorithm)

后端 未结 16 2937
我在风中等你
我在风中等你 2021-02-15 18:04

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         


        
相关标签:
16条回答
  • 2021-02-15 18:19

    Here's a python implementation, it should be easy enough to follow

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

    This outputs:

    1
    3-6
    8
    11-12
    14-16
    

    I know, I know, it isn't an algorithm, but I found it harder to actually explain it without having indentation problems than to just implement a solution for it.

    0 讨论(0)
  • 2021-02-15 18:19

    Python (>= 2.6)

    This version additionally handles duplicates and unsorted sequences.

    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
    

    Example:

    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])
    

    Output:

    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
    
    0 讨论(0)
  • 2021-02-15 18:23

    I believe the mergeinfo property that was introduced to Subversion in the 1.5 release has a format that is the same as what you're asking for, so you could potentially go look through the source of Subversion to find out how they do it. I'd be surprised if its any different than the other suggestions that have already been posted here.

    0 讨论(0)
  • 2021-02-15 18:24

    Here's my Perl solution. Could be cleaner and faster, but it shows how it works:

    # 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 = [ $_ ];
        }
    }
    
    0 讨论(0)
  • 2021-02-15 18:24

    I will assume the array X() is pre-sorted (and if not, sort the array before-hand).

    for each element of X() as $element (with $i as current array posistion)
        add $element to end of array Y()
        if (X($i) + 1 is less than X($i + 1)) AND ($i + 1 is not greater than sizeof(X())) then
            append Y(1)."-".Y(sizeof(Y())) to end of Z()
            unset Y()
        end if    
    next
    if anything remains in Y() append to end of Z()
    
    

    well, that's how I would do it.

    0 讨论(0)
  • 2021-02-15 18:26

    Here's a C# 3.0'y way of doing it:

    Points of interest:

    • automatic properties (public int Property { get; set; })
    • using new object initializers (new Range { Begin = xxx; ... }
    • using yield for lazy evaluation
    • using linq extension methods (First() and Skip())

    -

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

    Cheers, David

    0 讨论(0)
提交回复
热议问题