【问题标题】:How to group numbers in ranges using PHP如何使用 PHP 对范围内的数字进行分组
【发布时间】:2012-11-15 13:33:18
【问题描述】:

假设我在一个数组中有以下数字序列:

$numbers = array(1,3,2,23,24,25,26, 8)

如何在范围内打印它们,例如:

数字是 1-3、23-26、8。

【问题讨论】:

  • 分组逻辑不清楚。为什么是1-3 而不是1-26
  • 他想对所有数字都存在的序列进行分组
  • 以@zerkms 的优点为基础,您认为“超出范围”的范围是5 个数字太远了吗?还是只有连续的数字??
  • 8的位置重要吗?也就是说,您是否也接受 1-3、8、23-26 作为答案?
  • 是的,没错,抱歉没有更具体。我的意思是@emartel 说我需要按所有数字都存在的顺序对数字进行分组。

标签: php algorithm sorting


【解决方案1】:
$numbers = array(1,3,2,23,24,25,26,8);

$result = array();

$sorted = $numbers;
sort($sorted);

$current = null;
foreach ($sorted as $v) {
    if (is_null($current)) {
        $current = array($v, $v);
    } else {
        if ($current[1] + 1 == $v) {
            $current[1] = $v;
        } else {
            $result[] = $current;
            $current = array($v, $v);
        }
    }
}

$result[] = $current;

$arranged = array();

foreach ($numbers as $v) {
    foreach ($result as $k => $r) {
        if ($v >= $r[0] && $v <= $r[1]) {
            $arranged[] = $r;
            unset($result[$k]);
            break;
        }
    }
}

var_dump($arranged);

http://ideone.com/i2YGod

【讨论】:

    【解决方案2】:

    这是一个简单的版本,创建包含您的范围的 groups

    <?php
    $numbers = array(1,3,2,23,24,25,26,8);
    sort($numbers);
    $groups = array();
    
    for($i = 0; $i < count($numbers); $i++)
    {
        if($i > 0 && ($numbers[$i - 1] == $numbers[$i] - 1))
            array_push($groups[count($groups) - 1], $numbers[$i]);
        else // First value or no match, create a new group
            array_push($groups, array($numbers[$i])); 
    }
    
    foreach($groups as $group)
    {
        if(count($group) == 1) // Single value
            echo $group[0] . "\n";
        else // Range of values, minimum in [0], maximum in [count($group) - 1]
            echo $group[0] . " - " . $group[count($group) - 1] . "\n";
    }
    

    输出是

    1 - 3
    8
    23 - 26
    

    现在,如果您的范围的顺序很重要,就像您在问题中描述的那样,您仍然可以对您的组进行排序...从我所见,您希望范围首先是单个值吗?这可以通过添加来完成

    function groupRanges($a, $b)
    {
        if(count($a) == 1)
            if(count($b) == 1)
                return 0; // equal
            else
                return 1; // so $b is considered less than
    
        if(count($b) == 1)
            return -1; // so $a is considered less than
    
        return 0; // both are ranges, keep them there... could be adjusted to compare the size of each range
    }
    
    usort($groups, "groupRanges");
    

    就在foreach 之前,输出变为:

    1 - 3
    23 - 26
    8
    

    【讨论】:

    • 谢谢,这正是我需要的,我缺少的部分是在分组之前对数组进行排序。
    • 如果您想要范围 1、范围 2、单个数字,我只是添加了第二部分...通过修改 groupRanges 您可以控制范围在最终数组中的显示方式
    【解决方案3】:

    我知道我参加聚会有点晚了,但我必须这样做。

    <?php
    $numbers = array(1,3,2,23,24,25,26,8);
    sort($numbers);
    $x=0;
    $output = array();
    foreach($numbers as $k=>$n){
        if(isset($numbers[$k+1]) && $numbers[$k+1]==$n+1){
            $output[$x][]=$n;
        }elseif(isset($output[$x][count($output[$x])-1]) && $output[$x][count($output[$x])-1]+1==$n){
            $output[$x][]=$n;
        }else{
            $x++;
            $output[$x][] = $n;
            $x++;
        }
    }
    foreach($output as $o){
        echo $o[0];
        if(isset($o[count($o)-1]) && $o[count($o)-1]!=$o[0]){
            echo ' - '.$o[count($o)-1];
        }
        echo '<br>';
    }?>
    

    【讨论】:

      【解决方案4】:

      首先对数组进行排序,比如数组A。然后对其进行二分查找。 假设数组的大小为n:

      A[n/2]n/2 处的元素。

      如果A[n/2]-A[n/4] = n/2-n/4,那么 A[n/2]A[n/4] 在同一个组中 - 跳过 A[3n/8] 并检查 A[n/8] 以查看 是否A[n/2]-A[n/8] = n/2-n/8

      如果A[n/2]-A[n/4] != n/2-n/4,那么你手头有两组——一组有 A[n/2] in 和另一个有A[n/4] in。检查A[n/8] 看它是否在同一个组中 A[n/4]。检查A[3/8]是否与A[n/2]在同一组,如果不是,是否 在A[n/4] 组中。

      您也可以将它与排序算法结合使用,但我认为这不值得。这是快速的——在对数时间内并且是模块化的。

      【讨论】:

      • “对数”是指“O(N log N)”,对吗?仅靠排序就可以做到这一点。
      • 介于“纯”对数和线性之间——当整个数组是范围时,最好的情况是 O(log n),当每个单元格都是一个范围时,最坏的情况是 O(n),比如说所有偶数。但是,是的,排序算法设置了上限。希望这会有所帮助。
      【解决方案5】:
      $sorted = $numbers; 
      sort($sorted);
      $sorted[] = null;  # add a null so the last iteration stores the final range
      $ranges = array();
      $end = null;
      $start = null;
      
      foreach ($sorted as $x) {
          if ($start === null) {
              # first iteration.  New range, but there's no previous one to mention
              $start = $x;
          }
          elseif ($x !== $end + 1) {
              # non-contiguous values == new range.
              # squirrel away the one we were just working on, and start fresh
              $ranges[] = ($start === $end) ? "$start" : "$start-$end";
              $start = $x;
          }
          $end = $x;
      }
      
      $ranges_str = implode(', ', $ranges);
      

      【讨论】:

        【解决方案6】:

        昨天无法发布,所以我来晚了。认为它足够优雅,可以分享它。

        <?php
        
        $numbers = array(1, 3, 2, 23, 24, 25, 26, 8);
        sort($numbers);
        $result = array();
        
        while (count($numbers) > 0)
        {
            $begin = reset($numbers);
            $end = array_shift($numbers);
        
            while (in_array($end + 1, $numbers))
            {
                $end = array_shift($numbers);
            }
            $beginAndEnd = array_unique(array($begin, $end));
            $result[] = implode('-', $beginAndEnd);
        }
        var_dump($result);
        ?>
        

        【讨论】:

          【解决方案7】:
          function get_ranges($numbers) {
          sort($numbers);
          $ranges=array();
          while(current($numbers)){
          $start=current($numbers);
          $next=next($numbers);
          $current=$start;
          while( ($next-$current)==1){
              $current=$next;
              $next=next($numbers);
          }
          $ranges[]=array('start'=>$start,'end'=>$current);
          }
          
          return $ranges;
          
          }
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2018-12-27
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2014-07-23
            • 2012-08-29
            • 2010-10-03
            • 2012-07-29
            相关资源
            最近更新 更多