什么传递给递归函数来转换2-D阵列" 90度"

时间:2015-12-18 20:24:22

标签: c++ arrays pointers

我想使用递归来解决面试问题:

  

"给定由NxN矩阵表示的图像,其中图像中的每个像素是4个字节,写一个方法将图像旋转90度。你能这样做吗?"

我的想法是旋转" square"的最外面4个边。然后递归调用swap来旋转内部" square&#34 ;; 但是,我无法确定应该作为递归函数传入的第一个参数(我标记了它" ????",我应该填写什么?)

#include <iostream>
using namespace std;

const static int N = 4;

void swap(int** a, int length)
{
    if (length <= 1)
    {
        return;
    }

    int top[length];
    for (int i=0; i<length ; i++)
    {
        top[i] = a[0][i];
    }

    // left to top
    for (int i=0; i < length; i++)
    {
        a[0][i] = a[length-i-1][0];
    }

    // bottom to left
    for (int i=0; i < length; i++)
    {
        a[i][0] = a[length-1][i];
    }

    // right to bottom
    for (int i=0; i < length; i++)
    {
        a[length-1][i] = a[length-i-1][length-1];
    }

    // top to right
    for (int i=0; i < length; i++)
    {
        a[i][length-1]= top[i];
    }

    swap(????, length-2);
}

int main()
{
    int a[N][N] = {
        {1, 2, 3, 4},
        {5, 6, 7, 8},
        {9, 10, 11, 12},
        {13, 14, 15, 16}
    };

    int *b[N]; //surrogate
    for (size_t i=0;i<N; i++)
    {
        b[i] = a[i];
    }

    swap(b, N);
    return 0;
}

4 个答案:

答案 0 :(得分:3)

这是我的努力。计划是我们的旋转可以分解成只旋转四个像素的组(通过考虑每次旋转方形90度时映射的位置,从一个像素组成的组)。

#include <iostream>
#include <iomanip>

template<typename PixelT, size_t N>
void rotate(PixelT (&pixel)[N][N], size_t shell = 0)
{
// end recursion condition
    if ( shell >= N / 2 )
        return;

// These variables will be optimized out, have written them here
// explicitly so it is clear what is going on. They represent the
// coordinate of those named sides of the square we are working on.
    auto top = shell;
    auto left = shell;
    auto bottom = (N-1) - shell;
    auto right = (N-1) - shell;

// For each pixel on the top side, rotate the four pixels 
// it maps to under 90 degree rotation
    for (auto i = 0; i < right - left; ++i)
    {
    // Anti-clockwise
        auto tmp = pixel[top][left+i];
        pixel[top][left+i] = pixel[top+i][right];
        pixel[top+i][right] = pixel[bottom][right-i];
        pixel[bottom][right-i] = pixel[bottom-i][left];
        pixel[bottom-i][left] = tmp;
    }

// Rotate next shell in
    rotate(pixel, shell + 1);
}

template<typename PixelT, size_t N>
void dump(PixelT (&pixel)[N][N])
{
    std::cout << "[\n";
    for (auto&& row : pixel)
    {
        for (auto&& pix : row)
            std::cout << std::setw(4) << pix;

        std::cout << '\n';
    }
    std::cout << "]\n";
}

int main()
{
    uint32_t a[4][4] = {
        {1, 2, 3, 4},
        {5, 6, 7, 8},
        {9, 10, 11, 12},
        {13, 14, 15, 16}
    };

    dump(a);
    rotate(a);
    dump(a);
}

递归的使用有点似是而非,因为递归参数可以被一行for循环替换。

类似地,我实际使用的foor循环也可以用递归替换(所以我们为每个四像素集递归一次)

答案 1 :(得分:1)

有很多方法可以做到这一点,并非所有方法都是直截了当的,并且通常,在C ++中使用std::vectorstd::array会使任务变得更加容易。而且我认为你可以在没有递归的情况下做到这一点,只需要调用swap来完成嵌套for - 循环的工作。

请参阅:How to rotate a N x N matrix by 90 degrees?或下面的M.M's answer

但这是我为使你的方法发挥作用所做的:

#include <iostream>
using namespace std;

const static int N = 4;

void swap(int** a, int length)
{
    if(length <= 1)
    {
        return;
    }

    {
        std::vector<int> top(length); // variable-length arrays not allowed in C++

        for(int i = 0; i<length; i++)
        {
            top[i] = a[0][i];
        }

        // left to top
        for(int i = 0; i < length; i++)
        {
            a[0][i] = a[length - i - 1][0];
        }

        // bottom to left
        for(int i = 0; i < length; i++)
        {
            a[i][0] = a[length - 1][i];
        }

        // right to bottom
        for(int i = 0; i < length; i++)
        {
            a[length - 1][i] = a[length - i - 1][length - 1];
        }

        // top to right
        for(int i = 0; i < length; i++)
        {
            a[i][length - 1] = top[i];
        }
    }

    std::vector<int*> b(length - 2); // creating another surrogate array

    for(int i = 0; i < length - 2; i++)
        b[i] = a[i + 1] + 1; // making it represent the inner square

    swap(b.data(), length - 2);
}

int main()
{
    int a[N][N] = {
        {1, 2, 3, 4},
        {5, 6, 7, 8},
        {9, 10, 11, 12},
        {13, 14, 15, 16}
    };

    int *b[N]; //surrogate
    for(size_t i = 0; i<N; i++)
    {
        b[i] = a[i];
    }

    swap(b, N);

    for(int i = 0; i < N; i++)
    {
        for(int j = 0; j < N; j++)
            cout << a[i][j] << " ";

        cout << endl;
    }

    return 0;
}

答案 2 :(得分:0)

2015年12月12日 - 更新 - 见下文:9x9的“开发输出”

这是一个递归方法的示例。

我会说这是'不完全'就地,但接近。我选择将外围值捕捉到矢量,然后将它们踩到新的旋转位置。虽然这可以使用更多std :: swap()来实现,但我发现代码令人反感。最大矢量(外周长)的大小小于2d阵列(即4x4的12对16个元素)。我想std :: swap()也使用单个元素临时存储。所以,不是就地。

我使用std :: array()作为2d数组。这鼓励我在开发期间使用其他模板工具(std :: rotate(),range-for和其他一些)。最后,我选择了一种方法,我认为更符合您的问题陈述。唉,它没有利用它们......哦,好吧。

我有一个嵌套类。 Corner_t标识左上角或右下角。这应该定义周长,如“void recursiveRotate2d90(Corner_t&amp;,Corner_t&amp;)”所使用的那样,

#include <iomanip>   // std::setw()
//                   Note: <iomanip> pulled in std::array,
//                   but because your tools might not:
#include <array>     // std::array
#include <iostream>  // std::cout, std::endl
#include <vector>    // std::vector


class Arr2d_t
{
   // forward declaration (nested class)
   class Corner_t;

public:

   static const int N = 4; // can make N into template parameter
   //                         needs different initialization

   Arr2d_t(void)
      {
         init();
      }

   ~Arr2d_t(void) {
      // default std::array dtor does what is needed for cleanup
   };


   int nGet() { return (N); }


   int exec(void)
      {
         const std::string HDRLINE (" --------------");

         // class ctor initialized "std::array <std::array <int, N>, N> m_2d"
         std::cout << "\nas initialized by ctor: \n" << HDRLINE << std::endl;
         show();
         std::cout << HDRLINE << std::endl;

         // recursive, ~in-place, rotate starting at outer perimeter
         Corner_t ulc(0, 0, this, "ulc");
         Corner_t lrc(N-1, N-1, this, "lrc");

         std::cout << "\nrecursively rotate 2d array by 90 (clockwise) : " << std::flush;

         recursiveRotate2d90(ulc, lrc);

         std::cout << "\n" << HDRLINE << std::endl;
         show();
         std::cout << HDRLINE << std::endl;

         return (0);
      }

private:

   void show()
      {
         for(int r = 0; r < N; r++)
         {
            for(int c = 0; c < N; c++)
               std::cout << std::setw(3) << m_2d[r][c] << " ";
            std::cout << std::endl;
         }
      }

   void init()
      {
         int n = 1;
         for (size_t r=0; r<N; ++r)
            for (size_t c=0; c<N; ++c)
               m_2d[r][c] = n++;
      }

   // recursively rotate 2d array by 90 degrees
   void recursiveRotate2d90(Corner_t& ulCnr,   // Top    Left  Corner row,col
                            Corner_t& lrCnr)   // Bottom Right Corner row,col
      {
         int   topRow = ulCnr.top();
         int  leftCol = ulCnr.left();
         int   botRow = lrCnr.bot();
         int rightCol = lrCnr.right();

         int sz = botRow - topRow; // 4x4: 3 - 0 => 3  or internal 2x2: 2 - 1 = 1

         if (sz < 1)  return; // recursion exit

         {
            // build a simple list of the 'outer perimeter' elements
            std::vector<int>  perimeterValues;  // capture m_2d perimeter values

            // capture 2d array perimeter values
            {
               // top row
               for(int i = 0;  i < sz; ++i) {
                  perimeterValues.push_back ( m_2d [topRow]  [leftCol+i] ); // topRow   ++
               }

               // right col
               for(int i = 0;   i < sz;   ++i)
               {
                  perimeterValues.push_back ( m_2d [topRow+i] [rightCol]);  // rightCol ++
               }

               // bottom row
               for(int i = 0; i < sz;  ++i) {
                  perimeterValues.push_back ( m_2d [botRow]  [rightCol-i]); // botRow   --
               }

               // left col reverse
               for(int i = 0;   i < sz;   ++i) {
                  perimeterValues.push_back( m_2d  [botRow-i] [leftCol]);    // leftCol  --
               }
            }

            // overwrite the captured perimeter values to achieve the rotation desired
            {
               int  nxtOut = 0;
               // to right col - from top row
               for(int i = 0; i < sz; ++i)  m_2d [topRow+i] [rightCol]   = perimeterValues[nxtOut++];

               // to bottom row - from right col
               for(int i = 0; i < sz; ++i)  m_2d [botRow]   [rightCol-i] = perimeterValues[nxtOut++];

               // to left col -  from bottom row
               for(int i = 0; i < sz; ++i)  m_2d [botRow-i] [leftCol]    = perimeterValues[nxtOut++];

               // to top row - from left column
               for(int i = 0; i < sz; ++i)  m_2d [topRow]   [leftCol+i]  = perimeterValues[nxtOut++];
               //
            }
         } // dtor perimeterValues


         // repeat against next inner perimeter
         Corner_t ulc = ulCnr.rightDown();
         Corner_t brc = lrCnr.leftUp();

         recursiveRotate2d90 (ulc, brc); // recurse

      } //   void recursiveRotate2d90(Corner_t&, Corner_t& )


private: // data attributes

   std::array <std::array <int, N>, N> m_2d;


   void showVec1(std::vector<int> v)
      {
         std::cout << "\n";
         for (size_t i=0; i<v.size(); ++i)
            if(v[i]) std::cout << " " << v[i];
            else     std::cout << "?" << i;
         std::cout << std::endl;
         show();
      }

   void showVec2(std::vector<int> v2)
      {
         for (size_t i=0; i<v2.size(); ++i)
            if(v2[i]) std::cout << " " << std::setw(2) << v2[i] << "  ";
            else      std::cout << "?" << std::setw(2) << i;
         std::cout << std::endl;
      }

   // //////////////////////////////////////////////////////////////////////
   class Corner_t
   {
   public:
      //                      vvvvvvvvvvvvvv -- class instance to access
      Corner_t (int r, int c, Arr2d_t* arr2d, const std::string& lbl) :
         m_r (r),
         m_c (c),
         m_a2d(arr2d),
         m_lbl(lbl)
         {
         }
      ~Corner_t() { }

      inline int  r() { return (m_r); }
      inline int  c() { return (m_c); }

      inline int  left()  { return (m_c); }
      inline int  right() { return (m_c); }

      inline int  top() { return (m_r); }
      inline int  bot() { return (m_r); }

      // tlc
      Corner_t  rightDown() {
         Corner_t retVal(m_r+1, m_c+1, m_a2d, (m_lbl+'1'));
         return retVal;
      }
      // brc
      Corner_t  leftUp   () {
         Corner_t retVal(m_r-1, m_c-1, m_a2d, (m_lbl+'2'));
         return retVal;
      }

      void showCrnr(char k = '-') {
         std::cout << m_lbl << k << " Corner_t: " << this << "  "
                   << m_r << ", " << m_c << std::endl;
      }

   private:
      int          m_r;
      int          m_c;
      Arr2d_t*     m_a2d; // capture pointer to class
      std::string  m_lbl;

   }; // class Corner_t

}; // class Arr2d_t


// /////////////////////////////////////////////////////////////////////////
int t270(void)
{
   std::cout << "\nsizeof(Arr2d_t): " << sizeof(Arr2d_t) << std::endl;

   Arr2d_t   arr2d;
   int N = arr2d.nGet();

   std::cout << "class  Arr2d_t  has only 1 data attribute: "
             << "\n\n     std::array <std::array <int, N>, N>  m_2d; "
             << "\n\nwhere N is " << N << ".    instance sizeof(arr2d): "
             << sizeof(arr2d)  << std::endl;
   std::cout << "\n" << N << "x" << N << " * sizeof(int) "
             << "= " << N*N << " * " << sizeof(int)
             << " = " << N*N*sizeof(int) << "\n\n" << std::endl;

   int retVal = arr2d.exec();

   return (retVal);
}

输出:

sizeof(Arr2d_t): 64
class  Arr2d_t  has only 1 data attribute: 

     std::array <std::array <int, N>, N>  m_2d; 

where N is 4.    instance sizeof(arr2d): 64

4x4 * sizeof(int) = 16 * 4 = 64



as initialized by ctor: 
 --------------
  1   2   3   4 
  5   6   7   8 
  9  10  11  12 
 13  14  15  16 
 --------------

recursively rotate 2d array by 90 (clockwise) : 
 --------------
 13   9   5   1 
 14  10   6   2 
 15  11   7   3 
 16  12   8   4 
 --------------

以前是,相当无聊,看起来很容易。请注意,当我提交时,我删除了所有开发和诊断cout,因此没有提示中间操作或递归。

以下是我的代码的下一个版本(未包含)的输出,并演示了启用了开发cout的数独大小(9x9)2d矩阵的旋转!此外,此版本首先递归到最小的框,然后执行另一个框以相反的顺序旋转(最小的框到最大框,总共4个中间框)。

每个中间数组都会突出显示已旋转的“框”,因此您可以看到“进度”,即运算中的算法。

留意点......

 as initialized by ctor via init(): 
 --------------
  1   2   3   4   5   6   7   8   9 
 10  11  12  13  14  15  16  17  18 
 19  20  21  22  23  24  25  26  27 
 28  29  30  31  32  33  34  35  36 
 37  38  39  40  41  42  43  44  45 
 46  47  48  49  50  51  52  53  54 
 55  56  57  58  59  60  61  62  63 
 64  65  66  67  68  69  70  71  72 
 73  74  75  76  77  78  79  80  81 
 --------------

recursively rotate 2d array by 90 (clockwise) : 
recursion exit  sz: 0

sz: 3   ul 3,3,  lr 5,5

perimeter values:
 31   32   33   42   51   50   49   40  

  1    2    3    4    5    6    7    8    9  
 10   11   12   13   14   15   16   17   18  
 19   20   21   22   23   24   25   26   27  
 28   29   30   49.  40.  31.  34   35   36  
 37   38   39   50.  41   32.  43   44   45  
 46   47   48   51.  42.  33.  52   53   54  
 55   56   57   58   59   60   61   62   63  
 64   65   66   67   68   69   70   71   72  
 73   74   75   76   77   78   79   80   81  

sz: 5   ul 2,2,  lr 6,6

perimeter values:
 21   22   23   24   25   34   43   52   61   60   59   58   57   48   39   30  

  1    2    3    4    5    6    7    8    9  
 10   11   12   13   14   15   16   17   18  
 19   20   57.  48.  39.  30.  21.  26   27  
 28   29   58.  49   40   31   22.  35   36  
 37   38   59.  50   41   32   23.  44   45  
 46   47   60.  51   42   33   24.  53   54  
 55   56   61.  52.  43.  34.  25.  62   63  
 64   65   66   67   68   69   70   71   72  
 73   74   75   76   77   78   79   80   81  

sz: 7   ul 1,1,  lr 7,7

perimeter values:
 11   12   13   14   15   16   17   26   35   44   53   62   71   70   69   68   67   66   65   56   47   38   29   20  

  1    2    3    4    5    6    7    8    9  
 10   65.  56.  47.  38.  29.  20.  11.  18  
 19   66.  57   48   39   30   21   12.  27  
 28   67.  58   49   40   31   22   13.  36  
 37   68.  59   50   41   32   23   14.  45  
 46   69.  60   51   42   33   24   15.  54  
 55   70.  61   52   43   34   25   16.  63  
 64   71.  62.  53.  44.  35.  26.  17.  72  
 73   74   75   76   77   78   79   80   81  

sz: 9   ul 0,0,  lr 8,8

perimeter values:
  1    2    3    4    5    6    7    8    9   18   27   36   45   54   63   72   81   80   79   78   77   76   75   74   73   64   55   46   37   28   19  10  

 73.  64.  55.  46.  37.  28.  19.  10.   1. 
 74.  65   56   47   38   29   20   11    2. 
 75.  66   57   48   39   30   21   12    3. 
 76.  67   58   49   40   31   22   13    4. 
 77.  68   59   50   41   32   23   14    5. 
 78.  69   60   51   42   33   24   15    6. 
 79.  70   61   52   43   34   25   16    7. 
 80.  71   62   53   44   35   26   17    8. 
 81.  72.  63.  54.  45.  36.  27.  18.   9. 

 --------------
 73  64  55  46  37  28  19  10   1 
 74  65  56  47  38  29  20  11   2 
 75  66  57  48  39  30  21  12   3 
 76  67  58  49  40  31  22  13   4 
 77  68  59  50  41  32  23  14   5 
 78  69  60  51  42  33  24  15   6 
 79  70  61  52  43  34  25  16   7 
 80  71  62  53  44  35  26  17   8 
 81  72  63  54  45  36  27  18   9 
 --------------

答案 3 :(得分:0)

您可以在不为其他矩阵或行分配空间的情况下执行此转换,但仍需要至少一个临时变量来执行以下操作:

// rotate the corners of a 4x4 2D array:
temp = a[0][0];
a[0][0] = a[0][3];
a[0][3] = a[3][3];
a[3][3] = a[3][0];
a[3][0] = temp;

因此,您只需要从矩阵的子集开始迭代前一个过程,这要归功于旋转模拟法。例如,考虑5x5和4x4矩阵:并用*表示起始单元格:

* * * * o   * * * o    * are the starting cells of the cyclical swap
o * * o o   o * o o
o o o o o   o o o o
o o o o o   o o o o
o o o o o

为了概括公式,您可以将此视为矩阵元素在其中间居中的旋转。因此,对于矩阵NxN,我们有:

enter image description here

enter image description here

使用这种循环交换策略旋转2D数组的代码不需要递归,嵌套循环就足够了。

void rotate_counterclockwise( int ** a, size_t nCols ) {
    size_t nn = nCols - 1;
    size_t hh = nCols / 2;
    for ( size_t i = 0; i < hh; i++ ) {
        for ( size_t j = i; j < (nn - i); j++) {
            int temp      = a[i][j];        // top left
            a[i][j]       = a[j][nn-i];     // top right
            a[j][nn-i]    = a[nn-i][nn-j];  // bottom right
            a[nn-i][nn-j] = a[nn-j][i];     // bottom left
            a[nn-j][i]    = temp;
        }
    }
}

如果你想进行顺时针旋转,你只需要在前面的代码中将右上角元素与左下角元素交换,或者将它实现为交换为lambdas:

#include <functional>

auto ccw_swap = [] (int & v1, int & v2, int & v3, int & v4 ) {
    int temp = v1;  
    v1 = v2;
    v2 = v3;
    v3 = v4;
    v4 = temp;              
};

auto cw_swap = [] (int & v1, int & v2, int & v3, int & v4 ) {
    int temp = v1;  
    v1 = v4;
    v4 = v3;
    v3 = v2;
    v2 = temp;              
};

auto ud_swap = [] (int & v1, int & v2, int & v3, int & v4 ) {
    int temp = v1;  
    v1 = v3;
    v3 = temp;
    temp = v2;
    v2 = v4;
    v4 = temp;              
};

void rotate_loop( int ** a, size_t nCols, std::function< void (int & v1, int & v2, int & v3, int & v4 ) > inner_loop ) {
    size_t nn = nCols - 1;
    size_t hh = nCols / 2;
    for ( size_t i = 0; i < hh; i++ ) {
        for ( size_t j = i; j < (nn - i); j++) {
            inner_loop( a[i][j], a[nn-j][i], a[nn-i][nn-j], a[j][nn-i]);
        }
    }
}

请注意,对于180°旋转,由于特定的旋转模拟,该方案的效果较差。对于换位和水平或垂直镜像,该方案是不适用的。 作为进一步的概括,我建议考虑将矩阵存储在单个数组中(或更好的std::vector)。

void rotate_loop( int * a, size_t nCols, std::function< void (int & v1, int & v2, int & v3, int & v4 ) > inner_loop ) {
    size_t nn = nCols - 1;
    size_t n1 = nCols * nn;
    size_t n2 = nCols * nCols - 1;
    size_t hh = nCols / 2;
    for ( size_t i = 0, ni = 0; i < hh; i++ ) {
        for ( size_t j = i, nj = ni, nij = ni + j, nji = nj - i; j < (nn - i); j++) {
            inner_loop( a[nij],     //     = a[i + i*N + (j - i)]           = a[i*N + j]
                        a[nn+nji],  //     = a[N - 1 - i + i*N + (j-i)*N]   = a[N-1-i+j*N]  
                        a[n2-nij],  //     = a[N*N - 1 - i - i*N - (j-i)]   = a[N*N-1-i*N-j]
                        a[n1-nji]); //     = a[N*(N-1) + i - i*N - (j-i)*N] = a[N*(N-1)+i-j*N]
            nj += nCols;            //  nj = j*N
            nij++;                  // nij = i*N + j 
            nji += nCols;           // nji = j*N - i
        }
        ni += nCols;                //  ni = i*N
    }
}

enum direction { counterclockwise, clockwise, upside_down };

template < typename T >         
void rotate( T a, size_t nCols, direction angle = counterclockwise ){
    if ( !a ) return;
    switch ( angle ) {
        case counterclockwise:
            rotate_loop(a,nCols,ccw_swap);
            break;
        case clockwise:
            rotate_loop(a,nCols,cw_swap);
            break;
        case upside_down:
            rotate_loop(a,nCols,ud_swap);
            break;
    }
}

void show( int * a, size_t nCols ) {
    for(int i = 0; i < nCols; i++) {
        for(int j = 0; j < nCols; j++)
            std::cout << " " << *(a++);
        std::cout << std::endl;
    }
}