就地(固定空间)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, R和C之间的关系。假设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 = oc和nc = or ,因此将这些替换为nl ,
nl = oc x R + or -----> [eq 1]
在求解ol和nl之间的关系后,我们得到
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)
注意nl和ol的值永远不会超过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