dongxin1980 2015-11-16 21:30
浏览 22
已采纳

以螺旋顺序打印2D数组值

I want to print an array in spiral order. For arrays with sizes 3x3, 4x4, ...etc. my code works correctly, but for 3x5, 4x6 or 5x8 sizes the output is wrong, returning only the first iteration.

This is my simple code:

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] . ' ');
        }
    } 
 }

Example with 3 rows and 4 columns:

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

Expected result for this array is 0 1 2 3 13 23 22 21 20 10 11 12, but the output of my function stops after 10.

For 4 rows and 4 columns:

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

...it should return 0 1 2 3 13 23 33 32 31 30 20 10 11 12 22 21, and that is what my code returns.

But I want both cases to work with my code. How can I correct the code to also produce the correct output for the first, and other cases?

  • 写回答

1条回答 默认 最新

  • douguaidian8021 2015-11-17 12:23
    关注

    There are a few problems with your code:

    • it does not treat the four directions of traversal in the same way. You have four loops for these four directions, but in some you have <= as loop-end condition in others <, in some the condition is on something minus 1, in others not.

    • it has no provision for when all elements have been printed by the first or second inner loop, and thus the remaining loops will in some cases print already printed elements.

    • the outer loop condition does not check whether there are still columns that need traversal. It is not enough to test for such rows only.

    Although you could try to fix your code, I think it is better to start from scratch, taking into account that the solution should be symmetric for all four directions. This is an important intuitive reaction to develop: spot symmetries. This will lead to less code and fewer bugs.

    You want to traverse a dimension (row or column) in your array until you reach the border of the array or an element you already printed. Then you want to turn 90° to the right and repeat exactly the same logic over and over again. So if your code looks different for these different directions, something is not right.

    I will share two implementations. Both will use the concept of the "current" cell, and let it move around in spiral motion.

    The first solution treats going back or forward along a row with the same code, and similarly it has one piece of code for traversing a column forward or backward. So this solution has two inner loops, one for traversing along a row, and another for traversing along a column. The direction in which a row or column is traversed is kept in the $direction variable, which flips between 1 and -1 at each execution of the outer loop:

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

    Note the perfect symmetry between the first inner loop and the second inner loop.

    In a second solution, this use of symmetry is taken one step further, in order to replace the two inner loops with only one. For that to happen we must abandon the use of separate variables for rows and columns, and use the concept of a size related to a dimension:

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

    An extended version

    Upon request more than one year later: here is a version that allows one to choose the corner to start from, and whether to do it counter-clockwise instead of clockwise. This function will not print the result, but return a 1D array, with the spiral sequence. This way you can decide yourself what to do with the result: print it, or ... whatever.

    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
    }
    

    See it run on eval.in

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥100 set_link_state
  • ¥15 虚幻5 UE美术毛发渲染
  • ¥15 CVRP 图论 物流运输优化
  • ¥15 Tableau online 嵌入ppt失败
  • ¥100 支付宝网页转账系统不识别账号
  • ¥15 基于单片机的靶位控制系统
  • ¥15 真我手机蓝牙传输进度消息被关闭了,怎么打开?(关键词-消息通知)
  • ¥15 装 pytorch 的时候出了好多问题,遇到这种情况怎么处理?
  • ¥20 IOS游览器某宝手机网页版自动立即购买JavaScript脚本
  • ¥15 手机接入宽带网线,如何释放宽带全部速度