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

后端 未结 16 2893
我在风中等你
我在风中等你 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:28

    Perl 6

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

    C (gcc)

    It is similar to the Python's version.

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

    Example:

    /**
     * $ 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);
    }
    

    Output:

    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)
  • If the array is sorted, as in your example, I would define buckets containing a min and a max.

    Initialize: Create a bucket with a min and a max equal to the first value.

    Loop: Compare each value with the max of the current bucket. If it is equal to or 1 more than the current max, update the max. If it is more than 1 greater than the max, save the bucket to a list of buckets and start a new bucket.

    At the end you will have a set of buckets with a min and a max in each. If the min is the same as the max, print a single number (ie: in your example, the first bucket would have a min and a max of 1). If they are different, print as a range.

    Example implementation in 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> 
    

    Basically you end up with a list of things, where each thing has (lowest-in-bucket, highest-in-bucket). Those are your ranges.

    If the list is not already sorted, sort it first.

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

    first: sort second: tokenise then: print the first value and loop over subsequent ones. If the 'current' value is equal to the last value +1, skip it. Otherwise if you've skipped value print dash and the value, otherwise print a comma and repeat.

    That should do. Unless you wanted me to code up your homework for you? :)

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