三元组稀疏矩阵转置运算
1.关于稀疏矩阵的解释(定义)
矩阵中非零元素的个数远远小于矩阵元素的总数,并且非零元素的分布没有规律,通常认为矩阵中非零元素的总数比上矩阵所有元素总数的值小于等于0.05时,则称该矩阵为稀疏矩阵(sparse matrix),该比值称为这个矩阵的稠密度。
摘自:https://baike.so.com/doc/5775258-5988034.html
2.有关稀疏矩阵的空间利用率
若一个M*N阶,稠密度为3%的稀疏矩阵,用全存储方式申请空间,则空间利用率为3%。为了高效利用空间,我们处理稀疏矩阵时,仅存储有效数(即不为0的数据),同时为了不丢失信息,需要存储有效数据以及数据所在位置信息。通常以两种方式,三元组方式和十字交叉链表。
这里,我们采用三元组方式处理。
三元组方式,即用三个信息量表示矩阵中的一个有效元素,这三个信息量分别为:行,列,值
typedef struct TRIPLE {
int row; //行
int col; //列
USER_TYPE value; //值
}TRIPLE;
同样稀疏矩阵的控制头可以表示为:
typedef struct SPARSE_MATRIX {
TRIPLE *triple;
int maxRow;
int maxCol;
int count; //有效元素个数
}SPARSE_MATRIX;
关于用三元组方式表达稀疏矩阵,有一个默认的原则:
在三元组数组中,数据一定是按照行下标升序,行下标相同,则按列下标升序排列。
3.三元组稀疏矩阵转置算法
首先列出原稀疏矩阵,如下:
0 1 2 3 4 5 6
0 7 3 0 4 0 0 0
1 2 0 0 0 0 0 5
2 0 0 0 0 8 0 0
3 0 6 0 0 0 0 0
4 3 7 0 0 0 0 9
5 0 0 0 0 1 0 0
那么根据稀疏矩阵,我们可以列出有效数据所对应的行列值,并且列出转置后对应的行列值(即行列互换):
原行列值 转置后
0 0 0 7 0 0 0 7
1 0 1 3 1 0 1 2
2 0 3 4 2 0 4 3
3 1 0 2 3 1 0 3
4 1 6 5 4 1 3 6
5 2 4 8 5 1 4 7
6 3 1 6 6 3 0 4
7 4 0 3 7 4 2 8
8 4 1 7 8 4 5 1
9 4 6 9 9 6 1 5
10 5 4 1 10 6 4 9
通过上面的转置过程,我们可以发现转置后依然遵循默认原则,为此,可以总结出此转置算法的主要思路,即,遍历原三元组的列来作为转置后的行,但如何将其放置在合适的位置,也就是正确的下标对应的行呢?比如,我们原三元组的下标为[1]的列为1,我们应该如何知道将(0 1 3 )放置在转置后的下标为[3]的位置(并转化为1 0 3),而不是下标为[4]的位置呢。
也就是说,现在的问题转换为:在转置前,获取转置后每一行前面的有效元素个数。
要解决这个问题其实不难,主要有以下几个步骤:
1.我们需要准备一个大小为原稀疏矩阵最大列+1的数组,初值均为0;再依次遍历原三元组,以原三元组的列+1作为下标对应的数组元素值++;
static void getRowElementCount(const SPARSE_MATRIX *sm, int *array) {
int index;
for (index = 0; index < sm->count; index++) {
++array[sm->triple[index].col + 1];
}
}
2.将数组元素对应的值,向后滚加,此时数组元素的值就对应转置后的三元组那一行的前面的有效元素个数;
static void rollAdd(int *array, int arrayCount) {
int index;
for (index = 1; index < arrayCount; index++) {
array[index] += array[index - 1];
}
}
3.这一步很简单,也很重要,那就是每放置一个转置后的三元组之后,千万不要忘记给那个下标对应的数组元素值加1,这是为了给下一次转置做准备。
for (index = 0; index < resMatrix->count; index++) {
t = resMatrix->triple[index].col;
tagMatrix->triple[array[t]].row = resMatrix->triple[index].col;
tagMatrix->triple[array[t]].col = resMatrix->triple[index].row;
tagMatrix->triple[array[t]].value = resMatrix->triple[index].value;
array[t]++;
}
4.总结及代码和结果展示
至此,三元组稀疏矩阵转置算法的核心思路阐述就结束了。这里最最最重要的就是,对手工过程细致分析,因为手工过程的分析就是我们写代码的过程,此三元组稀疏矩阵转置运算,可谓将数组下标玩到了极致,大家可以仔细体会,就会和我有同感了。
再者,有关初始化以及销毁等一些准备工作,在这里就不再赘述了,下面就直接上代码啦!
sparseMatrix.h
#ifndef _MEC_SPARSE_MATRIX_H
#define _MEC_SPARSE_MATRIX_H
#include "mec.h"
typedef int USER_TYPE;
typedef struct TRIPLE {
int row; //行
int col; //列
USER_TYPE value; //值
}TRIPLE;
typedef struct SPARSE_MATRIX {
TRIPLE *triple;
int maxRow;
int maxCol;
int count; //有效元素个数
}SPARSE_MATRIX;
boolean initSparseMatrix(SPARSE_MATRIX **sm, int maxRow, int maxCol, int count);
void destorySparseMatrix(SPARSE_MATRIX **sm);
void showSparseMatrix(const SPARSE_MATRIX *sm);
SPARSE_MATRIX *revangeMatrix(const SPARSE_MATRIX *sm);
#endif
sparseMatrix.c
#include <stdio.h>
#include <malloc.h>
#include "mec.h"
#include "sparseMatrix.h"
static void revange(const SPARSE_MATRIX *resMatrix, const SPARSE_MATRIX *tagMatrix);
static void getRowElementCount(const SPARSE_MATRIX *sm, int *array);
static void rollAdd(int *array, int arrayCount);
boolean initSparseMatrix(SPARSE_MATRIX **sm, int maxRow, int maxCol, int count) {
if (NULL == sm || NULL != *sm || maxRow <= 0 || maxCol <= 0 || count <= 0) {
return FALSE;
}
*sm = (SPARSE_MATRIX *) calloc(sizeof(SPARSE_MATRIX), 1);
(*sm)->triple = (TRIPLE *) calloc(sizeof(TRIPLE), count);
(*sm)->maxRow = maxRow;
(*sm)->maxCol = maxCol;
(*sm)->count = count;
//从键盘输入行,列,值
//要求满足三元组默认原则,即,行升序,行相同,列升序
//直接在这里处理三元组数据的输入,是很粗糙的,有待改进
int row;
int col;
USER_TYPE value;
int index;
for (index = 0; index < count; index++) {
printf("请输入三元组数据:");
scanf("%d %d %d", &row, &col, &value);
(*sm)->triple[index].row = row;
(*sm)->triple[index].col = col;
(*sm)->triple[index].value = value;
}
return TRUE;
}
void destorySparseMatrix(SPARSE_MATRIX **sm) {
if (NULL == sm || NULL == *sm) {
return;
}
free((*sm)->triple);
free(*sm);
*sm = NULL;
}
void showSparseMatrix(const SPARSE_MATRIX *sm) {
int row;
int col;
int index = 0;
int value;
for (row = 0; row < sm->maxRow; row++) {
for (col = 0; col < sm->maxCol; col++) {
if (row == sm->triple[index].row && col == sm->triple[index].col) {
value = sm->triple[index++].value;
} else {
value = 0;
}
printf("%5d", value);
}
printf("\n");
}
}
SPARSE_MATRIX *revangeMatrix(const SPARSE_MATRIX *sm) {
SPARSE_MATRIX *result = NULL;
result = (SPARSE_MATRIX *) calloc(sizeof(SPARSE_MATRIX), 1);
result->triple = (TRIPLE *) calloc(sizeof(TRIPLE), sm->count);
result->maxRow = sm->maxCol;
result->maxCol = sm->maxRow;
result->count = sm->count;
revange(sm, result);
return result;
}
static void revange(const SPARSE_MATRIX *resMatrix, const SPARSE_MATRIX *tagMatrix) {
int *array = NULL;
int arrayCount = resMatrix->maxCol + 1;
int index;
int t;
array = (int *) calloc(sizeof(int), arrayCount);
getRowElementCount(resMatrix, array);
rollAdd(array, arrayCount);
for (index = 0; index < resMatrix->count; index++) {
t = resMatrix->triple[index].col;
tagMatrix->triple[array[t]].row = resMatrix->triple[index].col;
tagMatrix->triple[array[t]].col = resMatrix->triple[index].row;
tagMatrix->triple[array[t]].value = resMatrix->triple[index].value;
array[t]++;
}
free(array);
}
static void getRowElementCount(const SPARSE_MATRIX *sm, int *array) {
int index;
for (index = 0; index < sm->count; index++) {
++array[sm->triple[index].col + 1];
}
}
static void rollAdd(int *array, int arrayCount) {
int index;
for (index = 1; index < arrayCount; index++) {
array[index] += array[index - 1];
}
}
test.c
#include <stdio.h>
#include "sparseMatrix.h"
int main(int argc, char const *argv[]) {
SPARSE_MATRIX *smOne = NULL;
SPARSE_MATRIX *smTwo = NULL;
int maxRow;
int maxCol;
int count;
printf("请输入稀疏矩阵行阶、列阶和有效元素个数:");
scanf("%d %d %d", &maxRow, &maxCol, &count);
initSparseMatrix(&smOne, maxRow, maxCol, count);
showSparseMatrix(smOne);
smTwo = revangeMatrix(smOne);
printf("转置后:\n");
showSparseMatrix(smTwo);
destorySparseMatrix(&smOne);
destorySparseMatrix(&smTwo);
return 0;
}
运行结果如下:
中文编码有点问题,还没有妥善解决,请大家见谅!
有关三元组稀疏矩阵转置运算,简述至此。
若有错误,请指正,若有建议,请分享,谢谢!