矩阵的转置
2017-09-07 18:36
232 查看
稀疏矩阵:
当一个矩阵中的很多元素都是零,而且非零元素的分布没有规律时,该矩阵称为稀疏矩阵。
稀疏矩阵的压缩存储方法:
从稀疏矩阵的概念,我们可以知道,稀疏矩阵的大多元素都是零。所以,我们只需要存储非零元素即可。这时,我们引入三元组表的概念:三元组表是一个线性表,线性表中的每一个结点对应稀疏矩阵的一个非零元素。结点包括三个域,分别为非零元素的行下标、列下标和值。并且,结点按矩阵的行优先顺序排列。
【例】存在矩阵A。那么矩阵A是怎么存储的呢?
A矩阵的三元组表:索引为0时,存储的是稀疏矩阵的行数、列数和非零元素的个数。
表中结点类型定义如下:(JAVA)
矩阵类型定义如下:
矩阵的转置运算:
A的三元组表
B的三元组表
也就是说,矩阵的转置实际上是三元组表的改变。
【算法一】
算法思路:
①将两个矩阵的行数和列数相互交换;
②将每个三元组中的i和j互相调换;
③重排三元组之间的次序。
代码实现:(JAVA)
// 稀疏矩阵的转置
public static Matrix transpose(Matrix a) {
Matrix b = new Matrix(a.cols, a.rows, a.vals); // 转置后的矩阵b
int bindex = 1;
for(int col = 1; col <= a.cols; col++) { // 按a的列序转置
for(int aindex = 1; aindex <= a.vals; aindex++) { // 扫描整个三元组表
if(a.data[aindex].j == col) {
b.data[bindex].i = a.data[aindex].j;
b.data[bindex].j = a.data[aindex].i;
b.data[bindex].value = a.data[aindex].value;
bindex++;
}
}
}
return b;
}
核心代码讲解:
①我们遍历矩阵a的列序,保证转置之后的矩阵行优先的原则。
②扫描整个三元组,找到当前列的结点。(此时,浪费了一定的时间。因为对于每一个col,我们都需要遍历整个三元组,直到与其对应的为止。)
算法评价:
上述算法是在二重循环内完成的,算法的时间复杂度为O(cols*vals)。当非零元素的个数值vals=cols*rows时,时间复杂度为O(
)。可见,该算法虽然节省了空间,但时间复杂度提高了。所以,上述算法只适用于当vals<<rows*cols(非零元素较少)的情况。
【算法二:快速转置算法】
原理:
我们已经知道,矩阵的转置实际上是改变其对应的三元组。那么,我们事先能不能知道改变之后的三元组是什么样子呢?答案是肯定的。进一步,既然我们已经知道了转置之后的三元组,那么对于索引为1的第一个非零元素,我们可不可以直接确定它转置之后再哪一个位置呢?同理,索引为2的第二个非零元素,我们能不能直接确定它转置之后的位置呢?以此类推,如果我们知道了这样一个映射关系,我们就可以做到直接定位,把结点插入到新的位置。
举个例子:
我们去图书馆找书的时候,我们不会从图书馆的第一个书架开始,一本一本的去找。我们是怎么做的呢?根据书的编号,参考图书馆存放书的信息,直接定位到相应的书架去找!那么,在这个例子中,图书馆为大家提供的馆藏信息表(记录不同的书的存放位置)就显的很重要了。这个表,实际上就是一个映射关系。
那么,如果我们也构建这样一个映射关系表,是不是也可以做到直接定位了呢?答案是肯定的。
如何构建这样一个映射关系?
第一列的元素转置之后在第一行。那么如果第一列的非零元素有两个,那么他们肯定占据的是索引1和2位置。同理,第二列的非零元素如果有两个,占据的应该是索引3和4位置。以此类推,我们便可以确定转置之后的位置了。因此,我们需要记录:每一列非零元素的个数num[col]以及该列第一个非零元素的起始索引位置cpot[col]。
这里,还是以矩阵A举例。
从表格我们可以发现:cpot[col]=cpot[col-1]+num[col]
算法思路:
①构建映射关系。即初始化num[col]和cpot[col]。
②将结点放到指定的位置。
代码实现:
代码讲解:
这里,初始化数组的时候,多开辟一个空间,是因为三元数组表的第一个元素)(索引为0的位置)存储的是矩阵的行数、列数以及非零元素的个数。
(欢迎评论指导!转载时,请注明出处,谢谢。)
当一个矩阵中的很多元素都是零,而且非零元素的分布没有规律时,该矩阵称为稀疏矩阵。
稀疏矩阵的压缩存储方法:
从稀疏矩阵的概念,我们可以知道,稀疏矩阵的大多元素都是零。所以,我们只需要存储非零元素即可。这时,我们引入三元组表的概念:三元组表是一个线性表,线性表中的每一个结点对应稀疏矩阵的一个非零元素。结点包括三个域,分别为非零元素的行下标、列下标和值。并且,结点按矩阵的行优先顺序排列。
【例】存在矩阵A。那么矩阵A是怎么存储的呢?
A矩阵的三元组表:索引为0时,存储的是稀疏矩阵的行数、列数和非零元素的个数。
索引 | i | j | v |
0 | 5 | 6 | 6 |
1 | 1 | 1 | 3 |
2 | 1 | 6 | 7 |
3 | 2 | 3 | 6 |
4 | 3 | 1 | 2 |
5 | 3 | 2 | 3 |
6 | 5 | 5 | 2 |
package com.linearList.matrix; /* * 说明: 三元组的结点描述 * @author 秦霞爱朱剑锋 */ public class Node { int i; // 行下标 int j; // 列下标 int value; // 值 public Node() {} public Node(int i, int j, int vlaue) { this.i = i; this.j = j; this.value = vlaue; } }
矩阵类型定义如下:
package com.linearList.matrix; /* * 说明:矩阵的压缩存储描述(行优先) */ public class Matrix { final int MAX = 10; // 非零元素个数的最大值 int rows, cols, vals; // 分别为行、列、非零元素个数 Node data[] = new Node[MAX]; public Matrix(int rows, int cols, int vals) { this.rows = rows; this.cols = cols; this.vals = vals; // 初始化data[0]:data[0]的i,j,v分别存储稀疏矩阵的行数、列数和非零元素的个数 data[0] = new Node(rows, cols, vals); // 申请剩余空间 if(vals <= MAX) { for (int i = 1; i <= vals; i++) { data[i] = new Node(); } } } }
矩阵的转置运算:
A的三元组表
索引 | i | j | v |
0 | 5 | 6 | 6 |
1 | 1 | 1 | 3 |
2 | 1 | 6 | 7 |
3 | 2 | 3 | 6 |
4 | 3 | 1 | 2 |
5 | 3 | 2 | 3 |
6 | 5 | 5 | 2 |
索引 | i | j | v |
0 | 5 | 6 | 6 |
1 | 1 | 1 | 3 |
2 | 1 | 6 | 7 |
3 | 2 | 3 | 6 |
4 | 3 | 1 | 2 |
5 | 3 | 2 | 3 |
6 | 5 | 5 | 2 |
【算法一】
算法思路:
①将两个矩阵的行数和列数相互交换;
②将每个三元组中的i和j互相调换;
③重排三元组之间的次序。
代码实现:(JAVA)
// 稀疏矩阵的转置
public static Matrix transpose(Matrix a) {
Matrix b = new Matrix(a.cols, a.rows, a.vals); // 转置后的矩阵b
int bindex = 1;
for(int col = 1; col <= a.cols; col++) { // 按a的列序转置
for(int aindex = 1; aindex <= a.vals; aindex++) { // 扫描整个三元组表
if(a.data[aindex].j == col) {
b.data[bindex].i = a.data[aindex].j;
b.data[bindex].j = a.data[aindex].i;
b.data[bindex].value = a.data[aindex].value;
bindex++;
}
}
}
return b;
}
核心代码讲解:
①我们遍历矩阵a的列序,保证转置之后的矩阵行优先的原则。
②扫描整个三元组,找到当前列的结点。(此时,浪费了一定的时间。因为对于每一个col,我们都需要遍历整个三元组,直到与其对应的为止。)
算法评价:
上述算法是在二重循环内完成的,算法的时间复杂度为O(cols*vals)。当非零元素的个数值vals=cols*rows时,时间复杂度为O(
)。可见,该算法虽然节省了空间,但时间复杂度提高了。所以,上述算法只适用于当vals<<rows*cols(非零元素较少)的情况。
【算法二:快速转置算法】
原理:
我们已经知道,矩阵的转置实际上是改变其对应的三元组。那么,我们事先能不能知道改变之后的三元组是什么样子呢?答案是肯定的。进一步,既然我们已经知道了转置之后的三元组,那么对于索引为1的第一个非零元素,我们可不可以直接确定它转置之后再哪一个位置呢?同理,索引为2的第二个非零元素,我们能不能直接确定它转置之后的位置呢?以此类推,如果我们知道了这样一个映射关系,我们就可以做到直接定位,把结点插入到新的位置。
举个例子:
我们去图书馆找书的时候,我们不会从图书馆的第一个书架开始,一本一本的去找。我们是怎么做的呢?根据书的编号,参考图书馆存放书的信息,直接定位到相应的书架去找!那么,在这个例子中,图书馆为大家提供的馆藏信息表(记录不同的书的存放位置)就显的很重要了。这个表,实际上就是一个映射关系。
那么,如果我们也构建这样一个映射关系表,是不是也可以做到直接定位了呢?答案是肯定的。
如何构建这样一个映射关系?
第一列的元素转置之后在第一行。那么如果第一列的非零元素有两个,那么他们肯定占据的是索引1和2位置。同理,第二列的非零元素如果有两个,占据的应该是索引3和4位置。以此类推,我们便可以确定转置之后的位置了。因此,我们需要记录:每一列非零元素的个数num[col]以及该列第一个非零元素的起始索引位置cpot[col]。
这里,还是以矩阵A举例。
col | 1 | 2 | 3 | 4 | 5 | 6 |
num[col] | 2 | 1 | 1 | 0 | 1 | 1 |
cpot[col] | 1 | 3 | 4 | | 5 | 6 |
算法思路:
①构建映射关系。即初始化num[col]和cpot[col]。
②将结点放到指定的位置。
代码实现:
// 稀疏矩阵的快速转置算法 public static Matrix fastTranspose(Matrix a) { Matrix b = new Matrix(a.cols, a.rows, a.vals); int num[] = new int[a.cols + 1]; // 初始化 num[] for(int i = 1; i < a.vals; i++) { ++num[a.data[i].j]; } int cpot[] = new int[a.cols + 1]; // 初始化 cpot[] cpot[1] = 1; for(int col = 2; col < num.length; col++) { cpot[col] = cpot[col-1] + num[col]; } for(int index = 1; index <= a.vals; index++) { int col = a.data[index].j; int newIndex = cpot[col]; b.data[newIndex].i = a.data[index].j; b.data[newIndex].j = a.data[index].i; b.data[newIndex].value = a.data[index].value; cpot[col]++; } return b; }
代码讲解:
这里,初始化数组的时候,多开辟一个空间,是因为三元数组表的第一个元素)(索引为0的位置)存储的是矩阵的行数、列数以及非零元素的个数。
(欢迎评论指导!转载时,请注明出处,谢谢。)
相关文章推荐
- 10.稀疏矩阵的存储 转置 相乘
- matlab中矩阵的简单操作 --- 创建,索引,重排,转置,拆分,删除,扩转,压缩
- 数据结构 对称矩阵的压缩存储与稀疏矩阵的转置
- OJ 1987: C语言实验——矩阵转置
- 稀疏矩阵的转置
- 稀疏矩阵的快速转置
- 数据结构----稀疏矩阵的快速转置
- Java实现的矩阵类及矩阵的转置,加减乘和矩阵求逆
- 稀疏矩阵的压缩存储及转置
- ZOJ 2316 Matrix Multiplication(找规律)(矩阵和它的转置矩阵之积)
- 稀疏矩阵的压缩存储和转置
- 矩阵的转置
- python 矩阵转置transpose
- 1334: 矩阵转置
- 指针实现矩阵转置
- VIM--矩阵转置运算
- 矩阵原地转置,空间复杂度为O(1)(暂时先保存下来)
- 三元组表示矩阵,以及稀疏矩阵的转置
- 数组结构体实现稀疏矩阵转置 (sdut oj 3347)
- 1175: 矩阵转置(指针专题)