📜  就地(固定空间)M x N 大小的矩阵转置 |更新

📅  最后修改于: 2022-05-13 01:57:22.181000             🧑  作者: Mango

就地(固定空间)M x N 大小的矩阵转置 |更新

大约四个月的差距(缺少GFG),一个新的职位。给定一个 M x N 矩阵,在没有辅助存储器的情况下转置矩阵。使用辅助数组很容易转置矩阵。如果矩阵的大小是对称的,我们可以通过在其对角线上镜像 2D 数组来就地转置矩阵(自己试试)。如何就地转置任意大小的矩阵?请参阅以下矩阵,

a b c       a d g j
d e f  ==>  b e h k
g h i       c f i l
j k l

根据 C/C++ 中的 2D 编号,相应的位置映射如下所示,

Org element New
 0     a     0
 1     b     4
 2     c     8
 3     d     1
 4     e     5
 5     f     9
 6     g     2
 7     h     6
 8     i    10
 9     j     3
 10    k     7
 11    l    11

请注意,第一个和最后一个元素保留在其原始位置。我们可以很容易地看到变换形式很少有排列循环。

  • 1->4->5->9->3->1 – 总共 5 个元素形成循环
  • 2->8->10->7->6->2——另外5个元素形成循环
  • 0 – 自循环
  • 11 – 自循环

从上面的例子中,我们可以很容易地设计一个算法来沿着这些循环移动元素。我们如何生成置换循环?两个矩阵中的元素数量都是常数,由 N = R * C 给出,其中 R 是行数,C 是列数。位置ol (R x C 矩阵中的旧位置)的元素,移动到nl (C x R 矩阵中的新位置)。我们需要建立ol, nl, RC之间的关系。假设ol = A[or][oc] 。在 C/C++ 中,我们可以将元素地址计算为:

ol = or x C + oc (ignore base reference for simplicity)

它将被移动到转置矩阵中的新位置nl ,比如nl = A[nr][nc] ,或者用 C/C++ 术语

nl = nr x R + nc (R - column count, C is row count as the matrix is transposed)

观察, nr = ocnc = or ,因此将这些替换为nl

nl = oc x R + or -----> [eq 1]

在求解olnl之间的关系后,我们得到

ol     = or x C     + oc
ol x R = or x C x R + oc x R
       = or x N     + oc x R    (from the fact R * C = N)
       = or x N     + (nl - or) --- from [eq 1]
       = or x (N-1) + nl

或者,

nl = ol x R - or x (N-1)

注意nlol的值永远不会超过N-1 ,所以考虑两边的模除以 ( N-1 ),我们根据同余的性质得到以下内容,

nl mod (N-1) = (ol x R - or x (N-1)) mod (N-1)
             = (ol x R) mod (N-1) - or x (N-1) mod(N-1)
             = ol x R mod (N-1), since second term evaluates to zero
nl = (ol x R) mod (N-1), since nl is always less than N-1

好奇的读者可能已经观察到上述关系的重要性。每个位置都按 R 因子(行大小)缩放。从矩阵中可以明显看出,每个位置都被 R 的比例因子所取代。实际的乘数取决于 (N-1) 的同余类,即乘数可以是同等类的 -ve 和 +ve 值。因此,每个位置变换都是简单的模除法。这些模除法形成循环排列。我们需要一些簿记信息来跟踪已经移动的元素。这是就地矩阵变换的代码,

C++
// C++ program for in-place matrix transpose
#include 
#define HASH_SIZE 128
 
using namespace std;
 
// A utility function to print a 2D array of size nr x nc and base address A
void Print2DArray(int *A, int nr, int nc)
{
    for(int r = 0; r < nr; r++)
    {
        for(int c = 0; c < nc; c++)
        {
            cout< b; // hash to mark moved elements
 
    b.reset();
    b[0] = b[size] = 1;
    i = 1; // Note that A[0] and A[size-1] won't move
    while (i < size)
    {
        cycleBegin = i;
        t = A[i];
        do
        {
            // Input matrix [r x c]
            // Output matrix
            // i_new = (i*r)%(N-1)
            next = (i*r)%size;
            swap(A[next], t);
            b[i] = 1;
            i = next;
        }
        while (i != cycleBegin);
 
        // Get Next Move (what about querying random location?)
        for (i = 1; i < size && b[i]; i++)
            ;
        cout << endl;
    }
}
 
// Driver program to test above function
int main()
{
    int r = 5, c = 6;
    int size = r*c;
    int *A = new int[size];
 
    for(int i = 0; i < size; i++)
        A[i] = i+1;
 
    Print2DArray(A, r, c);
    MatrixInplaceTranspose(A, r, c);
    Print2DArray(A, c, r);
 
    delete[] A;
 
    return 0;
}
 
// This code is contributed by rrrtnx.


C
// Program for in-place matrix transpose
#include 
#include 
#include 
#define HASH_SIZE 128
 
using namespace std;
 
// A utility function to print a 2D array of size nr x nc and base address A
void Print2DArray(int *A, int nr, int nc)
{
    for(int r = 0; r < nr; r++)
    {
        for(int c = 0; c < nc; c++)
            printf("%4d", *(A + r*nc + c));
 
        printf("\n");
    }
 
    printf("\n\n");
}
 
// Non-square matrix transpose of matrix of size r x c and base address A
void MatrixInplaceTranspose(int *A, int r, int c)
{
    int size = r*c - 1;
    int t; // holds element to be replaced, eventually becomes next element to move
    int next; // location of 't' to be moved
    int cycleBegin; // holds start of cycle
    int i; // iterator
    bitset b; // hash to mark moved elements
 
    b.reset();
    b[0] = b[size] = 1;
    i = 1; // Note that A[0] and A[size-1] won't move
    while (i < size)
    {
        cycleBegin = i;
        t = A[i];
        do
        {
            // Input matrix [r x c]
            // Output matrix
            // i_new = (i*r)%(N-1)
            next = (i*r)%size;
            swap(A[next], t);
            b[i] = 1;
            i = next;
        }
        while (i != cycleBegin);
 
        // Get Next Move (what about querying random location?)
        for (i = 1; i < size && b[i]; i++)
            ;
        cout << endl;
    }
}
 
// Driver program to test above function
int main(void)
{
    int r = 5, c = 6;
    int size = r*c;
    int *A = new int[size];
 
    for(int i = 0; i < size; i++)
        A[i] = i+1;
 
    Print2DArray(A, r, c);
    MatrixInplaceTranspose(A, r, c);
    Print2DArray(A, c, r);
 
    delete[] A;
 
    return 0;
}


C++
#include 
#define HASH_SIZE 128
 
using namespace std;
 
typedef char data_t;
 
void Print2DArray(char A[], int nr, int nc) {
   int size = nr*nc;
   for(int i = 0; i < size; i++)
        cout< b; // hash to mark moved elements
 
   b.reset();
   b[0] = b[size] = 1;
   i = 1; // Note that A[0] and A[size-1] won't move
   while( i < size ) {
      cycleBegin = i;
      t = A[i];
      do {
         // Input matrix [r x c]
         // Output matrix
         // i_new = (i*r)%size
         next = (i*r)%size;
         swap(A[next], t);
         b[i] = 1;
         i = next;
      } while( i != cycleBegin );
 
      // Get Next Move (what about querying random location?)
      for(i = 1; i < size && b[i]; i++)
         ;
      cout << endl;
   }
}
 
void Fill(data_t buf[], int size) {
   // Fill abcd ...
   for(int i = 0; i < size; i++)
   buf[i] = 'a'+i;
 
   // Fill 0123 ...
   buf += size;
   for(int i = 0; i < size; i++)
      buf[i] = '0'+i;
}
 
void TestCase_01(void) {
   int r = 2, c = 10;
   int size = r*c;
   data_t *A = new data_t[size];
 
   Fill(A, c);
 
   Print2DArray(A, r, c), cout << endl;
   MatrixTransposeInplaceArrangement(A, r, c);
   Print2DArray(A, c, r), cout << endl;
 
   delete[] A;
}
 
int main() {
   TestCase_01();
 
   return 0;
}
 
// This code is contributed by rutvik_56.


C
#include 
#include 
#include 
#define HASH_SIZE 128
 
using namespace std;
 
typedef char data_t;
 
void Print2DArray(char A[], int nr, int nc) {
   int size = nr*nc;
   for(int i = 0; i < size; i++)
      printf("%4c", *(A + i));
 
   printf("\n");
}
 
void MatrixTransposeInplaceArrangement(data_t A[], int r, int c) {
   int size = r*c - 1;
   data_t t; // holds element to be replaced, eventually becomes next element to move
   int next; // location of 't' to be moved
   int cycleBegin; // holds start of cycle
   int i; // iterator
   bitset b; // hash to mark moved elements
 
   b.reset();
   b[0] = b[size] = 1;
   i = 1; // Note that A[0] and A[size-1] won't move
   while( i < size ) {
      cycleBegin = i;
      t = A[i];
      do {
         // Input matrix [r x c]
         // Output matrix
         // i_new = (i*r)%size
         next = (i*r)%size;
         swap(A[next], t);
         b[i] = 1;
         i = next;
      } while( i != cycleBegin );
 
      // Get Next Move (what about querying random location?)
      for(i = 1; i < size && b[i]; i++)
         ;
      cout << endl;
   }
}
 
void Fill(data_t buf[], int size) {
   // Fill abcd ...
   for(int i = 0; i < size; i++)
   buf[i] = 'a'+i;
 
   // Fill 0123 ...
   buf += size;
   for(int i = 0; i < size; i++)
      buf[i] = '0'+i;
}
 
void TestCase_01(void) {
   int r = 2, c = 10;
   int size = r*c;
   data_t *A = new data_t[size];
 
   Fill(A, c);
 
   Print2DArray(A, r, c), cout << endl;
   MatrixTransposeInplaceArrangement(A, r, c);
   Print2DArray(A, c, r), cout << endl;
 
   delete[] A;
}
 
int main() {
   TestCase_01();
 
   return 0;
}


输出
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



   1   7  13  19  25
   2   8  14  20  26
   3   9  15  21  27
   4  10  16  22  28
   5  11  17  23  29
   6  12  18  24  30

输出:

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

   1   7  13  19  25
   2   8  14  20  26
   3   9  15  21  27
   4  10  16  22  28
   5  11  17  23  29
   6  12  18  24  30

扩展:2013 年 3 月 17 日一些读者发现矩阵转置和字符串转换之间的相似性。在没有太多理论的情况下,我提出了问题和解决方案。在给定的元素数组中,例如 [a1b2c3d4e5f6g7h8i9j1k2l3m4]。将其转换为 [abcdefghijklm1234567891234]。该程序应该就地运行。我们需要的是一个就地转置。下面给出的是代码。

C++

#include 
#define HASH_SIZE 128
 
using namespace std;
 
typedef char data_t;
 
void Print2DArray(char A[], int nr, int nc) {
   int size = nr*nc;
   for(int i = 0; i < size; i++)
        cout< b; // hash to mark moved elements
 
   b.reset();
   b[0] = b[size] = 1;
   i = 1; // Note that A[0] and A[size-1] won't move
   while( i < size ) {
      cycleBegin = i;
      t = A[i];
      do {
         // Input matrix [r x c]
         // Output matrix
         // i_new = (i*r)%size
         next = (i*r)%size;
         swap(A[next], t);
         b[i] = 1;
         i = next;
      } while( i != cycleBegin );
 
      // Get Next Move (what about querying random location?)
      for(i = 1; i < size && b[i]; i++)
         ;
      cout << endl;
   }
}
 
void Fill(data_t buf[], int size) {
   // Fill abcd ...
   for(int i = 0; i < size; i++)
   buf[i] = 'a'+i;
 
   // Fill 0123 ...
   buf += size;
   for(int i = 0; i < size; i++)
      buf[i] = '0'+i;
}
 
void TestCase_01(void) {
   int r = 2, c = 10;
   int size = r*c;
   data_t *A = new data_t[size];
 
   Fill(A, c);
 
   Print2DArray(A, r, c), cout << endl;
   MatrixTransposeInplaceArrangement(A, r, c);
   Print2DArray(A, c, r), cout << endl;
 
   delete[] A;
}
 
int main() {
   TestCase_01();
 
   return 0;
}
 
// This code is contributed by rutvik_56.

C

#include 
#include 
#include 
#define HASH_SIZE 128
 
using namespace std;
 
typedef char data_t;
 
void Print2DArray(char A[], int nr, int nc) {
   int size = nr*nc;
   for(int i = 0; i < size; i++)
      printf("%4c", *(A + i));
 
   printf("\n");
}
 
void MatrixTransposeInplaceArrangement(data_t A[], int r, int c) {
   int size = r*c - 1;
   data_t t; // holds element to be replaced, eventually becomes next element to move
   int next; // location of 't' to be moved
   int cycleBegin; // holds start of cycle
   int i; // iterator
   bitset b; // hash to mark moved elements
 
   b.reset();
   b[0] = b[size] = 1;
   i = 1; // Note that A[0] and A[size-1] won't move
   while( i < size ) {
      cycleBegin = i;
      t = A[i];
      do {
         // Input matrix [r x c]
         // Output matrix
         // i_new = (i*r)%size
         next = (i*r)%size;
         swap(A[next], t);
         b[i] = 1;
         i = next;
      } while( i != cycleBegin );
 
      // Get Next Move (what about querying random location?)
      for(i = 1; i < size && b[i]; i++)
         ;
      cout << endl;
   }
}
 
void Fill(data_t buf[], int size) {
   // Fill abcd ...
   for(int i = 0; i < size; i++)
   buf[i] = 'a'+i;
 
   // Fill 0123 ...
   buf += size;
   for(int i = 0; i < size; i++)
      buf[i] = '0'+i;
}
 
void TestCase_01(void) {
   int r = 2, c = 10;
   int size = r*c;
   data_t *A = new data_t[size];
 
   Fill(A, c);
 
   Print2DArray(A, r, c), cout << endl;
   MatrixTransposeInplaceArrangement(A, r, c);
   Print2DArray(A, c, r), cout << endl;
 
   delete[] A;
}
 
int main() {
   TestCase_01();
 
   return 0;
}
输出
a   b   c   d   e   f   g   h   i   j   0   1   2   3   4   5   6   7   8   9


   a   0   b   1   c   2   d   3   e   4   f   5   g   6   h   7   i   8   j   9