稀疏矩阵:若一个矩阵中,绝大部分数值都为0,或者相等,只有极少数数值非0,那么这种矩阵被称为稀疏矩阵。
衡量一个矩阵是不是稀疏矩阵的一般性原则:稀疏因子:(非0数值个数 / 总个数 * 100%) < 0.15%
0 | 1 | 2 | 3 | 4 | 5 | 6 | |
0 | 0 | 4 | 0 | 0 | 0 | 1 | 0 |
1 | 0 | 0 | 0 | 0 | 9 | 0 | 0 |
2 | 0 | 2 | 0 | 8 | 0 | 0 | 0 |
3 | 0 | 6 | 0 | 0 | 3 | 0 | 7 |
4 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
5 | 0 | 0 | 0 | 0 | 5 | 0 | 0 |
从稀疏矩阵的定义看,若使用全存储方案,明显是极其耗费空间的!
在此介绍一种压缩存储方案:三元组法。
对于矩阵中的每一个元素,记录其行 ,列和值这三个数据, 称为三元数据;由于稀疏矩阵中存在多个非0元素,那么,这些元素的三元数据集合,称为三元组。
要求三元组中的数据,按行值升序, 行值相同的,按列值升序排列。
三元组下标 | 行值 | 列值 | 值 |
0 | 0 | 1 | 4 |
1 | 0 | 5 | 1 |
2 | 1 | 4 | 9 |
3 | 2 | 1 | 2 |
4 | 2 | 3 | 8 |
5 | 3 | 1 | 6 |
6 | 3 | 4 | 3 |
7 | 3 | 6 | 7 |
8 | 5 | 4 | 5 |
使用一个结构体来控制此三元组:
typedef struct TRIAD {
int row;
int col;
int value;
}
同样使用一个结构体来控制矩阵:
typedef struct MATRIX{
int count;
int MaxRow;
int MaxCol;
TRIAD *triad;
}MATRIX;
基于三元组的稀疏矩阵的转秩运算,即将上述矩阵转制成:
0 | 1 | 2 | 3 | 4 | 5 | |
0 | 0 | 0 | 0 | 0 | 0 | 0 |
1 | 4 | 0 | 2 | 6 | 0 | 0 |
2 | 0 | 0 | 0 | 0 | 0 | 0 |
3 | 0 | 0 | 8 | 0 | 0 | 0 |
4 | 0 | 9 | 0 | 3 | 0 | 5 |
5 | 1 | 0 | 0 | 0 | 0 | 0 |
6 | 0 | 0 | 0 | 7 | 0 | 0 |
为实现转秩,需进行以下操作:
- 申请一个maxCol + 1个元素的数组Index;
- 遍历原三元组,以列值+1为Index的下标, 给Index的元素加一
-
元素加一 0 0 0 + 1 + 1 + 1 0 0 + 1 0 + 1 + 1 + 1 0 + 1 0 + 1 Index下标 0 1 2 3 4 5 6 7 下标对应的值 0 0 3 0 1 3 1 1 将上述Index的值递加,得到:
0 1 2 3 4 5 6 7 0 0 3 3 4 7 8 9
上述的得到的Index数组的意义还有:未来转秩后的矩阵,下标为Index的那一行的第一个元素在目标三元组中的下标就是Index【index】
这句话该怎么理解呢?就是说将未转秩矩阵的列值做为下表,在Index数组中所对应的值,就是转秩后矩阵Index所对应的下标。
因为在转秩前三元组就是按升序排列的,所以Index数组对应的值必然是那一行的第一个元素,在填入元素后将Index数组的值加一,下次再碰到同一行的元素就会填到该行的第二个元素位置。语言是生硬的,下面给出一个例子,进行一下变量跟踪,立马就会明白!!!
Index数组下标 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
value | 0 | 0 | 3 | 3 | 4 | 7 | 8 | 9 |
值加一 | value + 1 | value+ 1 |
转秩前Index数组下标 | 转秩前三元组 | 转秩后Index数组下标 | 转职后三元组 |
0 | (0, 1, 4) | 0 | (1, 0, 4) |
1 | (0, 5, 1) | 1 | |
2 | (1, 4 ,9) | 2 | |
3 | (2, 1 ,2) | 3 | |
4 | (2, 3, 8) | 4 | |
5 | (3, 1, 6) | 5 | |
6 | (3, 4, 3) | 6 | |
7 | (3, 6, 7) | 7 | (5, 0, 1) |
8 | (5, 4, 5) | 8 |
如上两表所示:第一个转秩前三元组的列值为1 , 在Index数组中寻找下标为1的其所对应的值,为0;则表明该三元组转秩后在新的Index数组中位于下标为0的位置,将该三元组行列互换,填入相应的位置,并且给原Index数组下标为1的值加一即可。然后再对第二个三元组进行操作,其列值为5, 在原Index数组中寻找下标为5的其所对应的值,为7; 则表明该三原组转秩后在新的Index数组中位于下标为7的位置,将该三元组行列互换,填入相应位置,且给原Index数组下标为5的值加一。后续以此类推... ...
具体实现代码如下(C):
#include <stdio.h>
#include <malloc.h>
#include "mec.h"
#include "triad.h"
#include "usertype.h"
boolean initMatrix(MATRIX **matrix, TRIAD *triad, int count, int MaxRow, int MaxCol) {
if(NULL == matrix || NULL != *matrix
|| count <= 0 || MaxRow <= 0 || MaxCol <= 0
|| NULL == triad) {
return FALSE;
}
MATRIX *m = (MATRIX *) calloc(sizeof(MATRIX), 1);
m->count = count;
m->MaxRow = MaxRow;
m->MaxCol = MaxCol;
m->triad = (TRIAD *) calloc(sizeof(TRIAD), count);
int index;
for(index = 0; index < count; index++) {
m->triad[index].row = triad[index].row;
m->triad[index].col = triad[index].col;
m->triad[index].value = triad[index].value;
}
*matrix = m;
return TRUE;
}
boolean showMatrix(MATRIX *matrix) {
int i;
int j;
int index = 0;
printf("矩阵如下:\n");
for(i = 0; i < matrix->MaxRow; i++) {
for(j = 0; j < matrix->MaxCol; j++) {
if(i == matrix->triad[index].row && j == matrix->triad[index].col) {
printf("%4d", matrix->triad[index].value);
index++;
} else {
printf("%4d", 0);
}
}
printf("\n");
}
return TRUE;
}
void destoryMatix(MATRIX **matrix) {
if(NULL == matrix || NULL == *matrix) {
return;
}
free((*matrix)->triad);
free(*matrix);
*matrix = NULL;
printf("销毁成功!!!");
}
void getMatrix(MATRIX **matrix) {
int MaxRow;
int MaxCol;
int count;
int index;
int row;
int col;
int value;
printf("请输入最大行列数:\n");
scanf("%d%d", &MaxRow, &MaxCol);
printf("请输入有效元素个数:\n");
scanf("%d", &count);
TRIAD *triad = (TRIAD *) calloc(sizeof(TRIAD), count);
printf("请输入有效元素行列及值:\n");
for(index = 0; index < count; index++) {
printf("第%d/%d个有效元素", index + 1, count);
scanf("%d%d%d", &row, &col, &value);
triad[index].row = row;
triad[index].col = col;
triad[index].value = value;
}
initMatrix(matrix, triad, count, MaxRow, MaxCol);
free(triad);
}
MATRIX *revangeMatrix(MATRIX *matrix) {
MATRIX *result = (MATRIX *) calloc(sizeof(MATRIX), 1);
result->triad = (TRIAD *) calloc(sizeof(TRIAD), matrix->count);
result->count = matrix->count;
result->MaxRow = matrix->MaxCol;
result->MaxCol = matrix->MaxRow;
int *index = (int *) calloc(sizeof(TRIAD), (matrix->MaxCol) + 1);
int i;
for(i = 0; i < matrix->count; i++) {
index[matrix->triad[i].col + 1]++;
}
for(i = 1; i< matrix->MaxCol + 1; i++) {
index[i] += index[i - 1];
printf("%d", index[i]);
}
int t;
TRIAD triad;
for(i = 0; i < matrix->count; i++) {
triad = matrix->triad[i];
t = index[triad.col];
result->triad[t].row = triad.col;
result->triad[t].col = triad.row;
result->triad[t].value = triad.value;
index[triad.col]++;
}
free(index);
return result;
}