一题多解 -- 循环填充二维数组

1 篇文章 0 订阅

问题描述

问题:有一个m*n的二维数组,从外到内按照顺时针填充,并将结果打印出来。

据个例子: 3 * 4 的数组,填充结果为

    1   2   3   4
  10  11  12  5

    9   8   7   6


解题思路1

我们可以来的直接点,既然题目要求我们顺时针填充,那么我们就这样把这个数组填充满就好了。

遇到的问题是:如何找到下一个应该填充的数组的下标

一般情况下,下标的递增都是线性单向的。例如x作为下标,循环的过程中就是x++之类的。

但是,这个问题无非是把一个线性的递增关系变成了一个区间函数,对于不同的情况,我们递增偏差不同。

那二维数组举例,对于从左到右,假设原始坐标是(x,y),那么下一个坐标是(x,y+1)。

依次来说:从上到下  (x,y) -> (x+1,y)

                    从右到左 (x,y) -> ( x, y - 1 )

                   从下到上  (x,y) -> (x-1,y)

所以,我们可以总结出这样的递增数据:

int directionMap[][2] = {
        { 0 , 1 },   // left -> right
        { 1 , 0 },   // top  -> bottom
        { 0 , -1 },  // right -> left
        { -1 , 0 },  // bottom -> top
    };

而且,我们可以发现,由于是顺时针填充,所以,填充的变换过程,永远按照directionMap从大到小的顺序变化。

于是,有如下的程序:


#include <iostream>  
#include <iomanip>
#include <memory>
using namespace std;  
  
  
namespace CyclePrint  
{  
    // map the two dimensional array index to flat index  
    class IndexMap  
    {  
    public:  
        IndexMap( int Column )  
            :m_nColumn(Column) {}  
  
        inline int operator()( int X, int Y ) const  
        {  
            return X * m_nColumn + Y;  
        }  
    private:  
        int m_nColumn;  
    };  

    void DeleteArray( int* array )
    {
        if( array )
        {
            delete [] array;
        }
    }
  
    void DoPrint( int Row , int Column )  
    {  
        if ( Row <= 0 || Column <= 0 )  
            return ;  
  
        // create a flat array to store the data  
        shared_ptr<int> matrixAutoRelease( new int[Row * Column] , DeleteArray );  

        int * matrix = matrixAutoRelease.get();  
        memset( matrix , 0 , sizeof(int) * Row * Column );  
  
        int directionMap[][2] = {  
            { 0 , 1 },   // left -> right  
            { 1 , 0 },   // top  -> bottom  
            { 0 , -1 },  // right -> left  
            { -1 , 0 },  // bottom -> top  
        };  
  
        int currentNumber = 1;  
        int currentDirection = 0;  
        IndexMap im(Column);  
        int x(0) , y(0);  
        while( currentNumber <= Row * Column )  
        {  
            if( x >= Row || x < 0 || y >= Column || y < 0 || matrix[im(x,y)] != 0 )  
            {  
                // revert the wrong move  
                x -= directionMap[currentDirection][0];  
                y -= directionMap[currentDirection][1];  
                // modify direction  
                currentDirection = ( currentDirection + 1) % 4;  
                // step to the correct index  
                x += directionMap[currentDirection][0];  
                y += directionMap[currentDirection][1];  
            }  
            else  
            {  
                matrix[im(x,y)] = currentNumber++ ;  
                x += directionMap[currentDirection][0];  
                y += directionMap[currentDirection][1];  
            }  
        }  
  
        for ( int i = 0 ; i < Row ; ++i )  
        {  
            for ( int j = 0 ; j < Column ; ++j )  
            {  
                cout << setw(4) << matrix[im(i,j)];  
            }  
            cout << endl;  
        }  
  
    }  
  
  
};  
  
  
  
int main(int argc, char* argv[])  
{  
    CyclePrint::DoPrint( 3 , 4 );  
    return 0;  
}  




解题思路2


通过一定的观察,我们发现:

当我们把大小为m*n,初始坐标点为(x,y)的一个二维矩阵的一圈填满之后,

剩下的任务是继续填满一个大小为(m-2)*(n-2),初始坐标为(x+1,y+1)


所以,这个问题也适用于递归的方式解决办法,我们只要精确的找对的递归出口的条件就好。

下面,考虑到递归的方式比较适用于FP思维,所以,用F#的方式给出递归的实现。

主要思路已经说明了,关于具体的解法,我这里首先根据输入参数 初始下标x,y和要填充的矩阵大小,映射成对应的填充顺序坐标。

然后直接递归子矩阵,知道找出所有的顺序坐标,随后填充到一个二维数组里面,最后输出。

MapToCoordinate为主要的映射函数。

// Learn more about F# at http://fsharp.net
module CyclePrint

type Direction = 
    | Left2Right
    | Top2Bottom
    | Right2Left
    | Bottom2Top


let rec MapToCoordinate x y row column =
    let MakeCoordinateFromDirection direction=
        match direction with
        | Left2Right when column > 0 && row > 0 ->
            [ 0 .. column - 1 ] |> List.map ( fun offset -> ( x , y + offset) )
        | Top2Bottom when column > 0 && row > 2 ->
            [ 0 .. row - 2 - 1 ] |> List.map ( fun offset -> ( x + 1 + offset, y + column - 1 ) )
        | Right2Left when column > 0 && row > 1  ->
            [ 0 .. -1 .. -( column - 1 ) ] |> List.map ( fun offset -> ( x + row - 1  , y + column - 1 + offset) )
        | Bottom2Top when column > 0 && row > 2 ->
            [ 0 .. -1 .. - ( row - 2 - 1) ] |> List.map ( fun offset -> ( x + row - 1 - 1 + offset, y  ) )
        | _ ->
            []
    let head = [ Left2Right ; Top2Bottom ; Right2Left ; Bottom2Top ]
                |> List.map MakeCoordinateFromDirection
                |> List.fold (fun acc elem -> (List.append acc elem) ) [] 

    match head with
        | [] -> []
        | _ -> List.append head (MapToCoordinate (x+1) (y+1) (row-2) (column-2))
            
    
let DoPrint row column = 
    let matrix = Array2D.create row column 0
    MapToCoordinate 0 0 row column 
        |> List.zip [ 1 .. (row*column) ] 
        |> List.iter (fun ( num , (x,y) ) -> matrix.[x,y] <- num ) 
    for x in 0..(row-1) do
        for y in 0..(column-1) do
            printf "%4d" matrix.[x,y]
        printfn ""


[<EntryPoint>]
let main ( args : string[] ) =
    DoPrint  4 8
    0



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值