【问题标题】:Print 2D array values in spiral sequence以螺旋顺序打印二维数组值
【发布时间】:2015-11-16 21:30:50
【问题描述】:

我想以螺旋顺序打印一个数组。对于大小为 3x3、4x4 等的数组。我的代码可以正常工作,但是对于 3x5、4x6 或 5x8 尺寸的输出是错误的,只返回第一次迭代。

这是我的简单代码:

private function _spiral($rows, $cols, array $array) {
    $offset = 0;
    while($offset < ($rows - 1)){
        for($col = $offset; $col <= $cols - 1; $col++){
            print($array[$offset][$col] . ' ');
        }
        $offset++;
        $cols--;
        for($row = $offset; $row < $rows; $row++){
            print($array[$row][$cols] . ' '); 
        }
        $rows--;
        for($col = $cols - 1; $col >= $offset; $col--){
            print($array[$rows][$col] . ' ');
        }
        for($row = $rows; $row >= $offset; $row--){
            print($array[$row][$offset - 1] . ' ');
        }
    } 
 }

以 3 行 4 列为例:

$array = array(
    array(00,01,02,03),
    array(10,11,12,13),
    array(20,21,22,23)
)

此数组的预期结果是0 1 2 3 13 23 22 21 20 10 11 12,但我的函数的输出在 10 点后停止。

对于 4 行 4 列:

$array = array(
    array(00,01,02,03),
    array(10,11,12,13),
    array(20,21,22,23),
    array(30,31,32,33)
)

...它应该返回0 1 2 3 13 23 33 32 31 30 20 10 11 12 22 21,这就是我的代码返回的内容。

但我希望这两种情况都适用于我的代码。如何更正代码以在第一种情况和其他情况下也产生正确的输出?

【问题讨论】:

  • 你能解释一下(在问题中,而不是评论)你想要达到的目标吗?
  • 您问题中的输出是否正确?好像错了(即使是你说的好)……
  • 我会假设您给出的“正确”解决方案实际上是不正确的,因为它跳过了第一列中的所有零。正确的解决方案最终应该遍历所有值。我只是将其作为一个小练习,发现定义变量$top$bottom$left$right 要容易得多。然后想想我要去的方向(从左到右,从上到下......等等),然后从我刚刚移动的任何一侧添加/减去。

标签: php


【解决方案1】:

您的代码存在一些问题:

  • 它不会以相同的方式对待遍历的四个方向。对于这四个方向,您有四个循环,但在某些情况下,&lt;= 作为循环结束条件,在其他情况下,&lt;,在某些情况下是负 1,在其他情况下不是。

  • 它没有规定第一个或第二个内部循环何时打印所有元素,因此在某些情况下剩余的循环将打印已打印的元素。

  • 外循环条件不检查是否还有需要遍历的列。仅测试这些行是不够的。

虽然您可以尝试修复您的代码,但我认为最好从头开始,考虑到解决方案应该在所有四个方向上都是对称的。这是一个重要的直观反应:点对称性。这将导致更少的代码和更少的错误。

您想遍历数组中的一个维度(行或列),直到到达数组的边界或已打印的元素。然后你想向右转 90°,一遍又一遍地重复完全相同的逻辑。因此,如果您的代码在这些不同的方向上看起来不同,则说明有问题。

我将分享两个实现。两者都将使用“当前”单元的概念,并让它以螺旋运动的方式移动。

第一个解决方案使用相同的代码处理沿行向后或向前移动,类似地,它有一段代码用于向前或向后遍历列。所以这个解决方案有两个内部循环,一个用于沿行遍历,另一个用于沿列遍历。行或列被遍历的方向保存在$direction变量中,在每次执行外循环时在1和-1之间翻转:

function _spiral(array $array) {
    // No need to have the number of rows and columns passed as arguments:
    // We can get that information from the array:
    $rows = count($array);
    $cols = count($array[0]);
    // Set "current" cell to be outside array: it moves into it in first inner loop
    $row = 0;
    $col = -1;
    $direction = 1; // Can be 1 for forward and -1 for backward
    while ($rows > 0 and $cols > 0) {
        // Print cells along one row
        for ($step = 0; $step < $cols; $step++) {
            $col += $direction;
            print $array[$row][$col] . ' ';
        }
        // As we have printed a row, we have fewer rows left to print from:
        $rows--;
        // Print cells along one column
        for ($step = 0; $step < $rows; $step++) {
            $row += $direction;
            print $array[$row][$col] . ' ';
        }
        // As we have printed a column, we have fewer columns left to print from:
        $cols--;
        // Now flip the direction between forward and backward
        $direction = -$direction;
    }
}

注意第一个内环和第二个内环之间的完美对称。

在第二种解决方案中,这种对称性的使用更进一步,以便仅用一个替换两个内部循环。为此,我们必须放弃对行和列使用单独的变量,并使用与维度相关的大小的概念:

function _spiral(array $array) {
    // This version of the function aims to treat rows and columns in the same way,
    // They are just another dimension, but all the logic is exactly the same:
    // $size[] has the number of rows in $size[0] and number of columns in $size[1]
    $size = Array(count($array), count($array[0]));
    // $current[] has the current row in $current[0] and current column in $current[1]
    $current = Array(0, -1);
    // $direction[] has the current row-traversal direction in $direction[0] 
    //    and column-traveral direction in $direction[1]
    $direction = Array(1, 1);
    $dimension = 0; // Which dimension to traverse along, can be 0 for row, 1 for column
    while ($size[$dimension] > 0)   {
        // Switch dimension (row to column, column to row), to traverse along
        $dimension = 1 - $dimension;
        // Print one line along that dimension, in its current direction
        for ($step = 0; $step < $size[$dimension]; $step++) {
            $current[$dimension] += $direction[$dimension];
            print $array[$current[0]][$current[1]] . ' ';
        }
        // As we have printed a line, we have fewer left to print from:
        $size[1 - $dimension]--;
        // Now flip the direction between forward and backward for this dimension:
        $direction[$dimension] = -$direction[$dimension];
    }
}

扩展版

一年多后应要求:这里有一个版本,可以选择从哪个角开始,以及是否逆时针而不是顺时针。这个函数不会打印结果,而是返回一个一维数组,带有螺旋序列。通过这种方式,您可以自己决定如何处理结果:打印它,或者……随便什么。

function spiral(array $array, $startRight = false, $startBottom = false, 
                              $counterClockWise = false) {
    // This version allows to select which corner to start from, and in which direction.
    //   $size[] has the number of rows in $size[0] and number of columns in $size[1]
    $size = [count($array), count($array[0])];
    // $direction[] has the current row-traversal direction in $direction[0] 
    //    and column-traversal direction in $direction[1]
    $direction = [$startBottom ? -1 : 1, $startRight ? -1 : 1];
    // Which dimension to traverse along: false means row, true means column.
    //   Every one of the optional arguments will flip the first dimension to use:
    $dimension = ($startBottom xor $startRight xor $counterClockWise);
    // $current[] has the current row in $current[0] and current column in $current[1]
    $current = [$startBottom * (count($array)-1), $startRight * (count($array[0])-1)];
    // Go back one step, outside of the grid
    $current[!$dimension] -= $direction[!$dimension];
    while ($size[$dimension] > 0)   {
        // Switch dimension (row to column, column to row), to traverse along
        $dimension = !$dimension;
        // Print one line along that dimension, in its current direction
        for ($step = 0; $step < $size[$dimension]; $step++) {
            $current[$dimension] += $direction[$dimension];
            $result[] = $array[$current[0]][$current[1]]; // store in new array
        }
        // As we have printed a line, we have fewer left to print from:
        $size[!$dimension]--;
        // Now flip the direction between forward and backward for this dimension:
        $direction[$dimension] = -$direction[$dimension];
    }
    return $result; // Return the resulting spiral as a 1D array
}

看到它在eval.in上运行

【讨论】:

  • 我不认为我自己会用到这个,但它非常聪明和紧凑。您是否可以修改函数以逆时针方向遍历,或从用户定义的角开始?我今天打算对此有所了解,但我有一种感觉,如果它可以做得很好,你会是做这件事的人(而且时间比我少得多)。为您的时尚功能点赞。
  • 看看答案的补充 ;-)
  • ...正如我所怀疑的那样——对你来说太容易了!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-03-23
  • 1970-01-01
  • 2016-02-14
  • 2016-05-18
  • 1970-01-01
相关资源
最近更新 更多