稀疏矩阵
当矩阵中只有极少的非零元素,而且分布也不规律,如果非零元素个数只占矩阵元素总数的25%~30%或低于这个百分数时,这样的矩阵称为稀疏矩阵。
三元组顺序表
对于稀疏矩阵中的非零元素来说,行号,列号以及元素值三项值可以唯一地确定该元素。元组顺序表中的三元恰好反映了这三项值,即(row,col,value),row代表行号,col代表列号,value代表元素值。然而,这样仍不能唯一地确定一个稀疏矩阵。对于三元组表来说,还必须给出矩阵的总行数,总列数以及非零元素的个数,这样才能唯一地确定一个稀疏矩阵。
顺序存储结构
typedef struct
{
int row, col;
DataType value;
}Triple;
typedef struct
{
Triple data[MAXSIZE + 1]; // 一维数组
int rows, cols, nums; //矩阵的行数,列数和非零元素个数
}TSMatrix;
转置运算
(1)按列序递增进行转置
算法思想
① 扫描原三元组,并按照先列序后行序的原则进行。即第一遍从原三元组的第一行开始向下搜索列号为1的元素,只要找到则顺序存入转置后的三元组表;
② 第2遍仍然从原三元组的第一列开始向下搜索列号为2的元素,只要找到则顺序存入转置后的三元组表;
......
③ 第n遍(n代表原三元组总共有多少列)将列号为n的元素,依次填入转置后的三元组表。
主要介绍原始算法和改进后的算法
// 转置运算(扫描三元组,按照先列序后行序的原则进行)
void TransposeTSMatrix1 (TSMatrix *A, TSMatrix *B)
{
int i, j, k;
B->rows = A->cols;
B->cols = A->rows;
B->nums = A->nums;
if (B->nums >0)
{
k = 1;
for (i = 1; i <= A->cols; i++) // A的列数就是B的行数,对应要循环找几次B的行号
{
for (j = 1; j <= A->nums; j++)
{
if (A->data[j].col == k) // 每次将A中列号和K相等的赋值到A的行号去
{
B->data[k].row = A->data[j].col;
B->data[k].col = A->data[j].row;
B->data[k].value = A->data[j].value;
k++;
}
}
}
}
}
// 改进后的按列序递增进行矩阵转置
// 就是利用非零元素的个数加以控制,让外重循环提前结束
void TransposeTSMatrix2 (TSMatrix *A, TSMatrix *B)
{
int i, j, k;
B->rows = A->cols;
B->cols = A->rows;
B->nums = A->nums;
if (B->nums >0)
{
k = 1;
for (i = 1; i <= A->cols; i++) // A的列数就是B的行数,对应要循环找几次B的行号
{
for (j = 1; j <= A->nums; j++)
{
if (A->data[j].col == k) // 每次将A中列号和K相等的赋值到A的行号去
{
B->data[k].row = A->data[j].col;
B->data[k].col = A->data[j].row;
B->data[k].value = A->data[j].value;
k++;
}
if (k > A->nums)
break;
}
}
}
}
(2)一次定位快速转置
算法思想
该算法是对被转置矩阵的三元组表只扫描一次,使得所有的非零元素一次性存放到转置后的三元组表中。
主要就在于num[col](存放第col列元素个数)和position[col](转置后第col行的开始位置)
① 扫描原矩阵A的三元组表,统计出其中每一列的非零元素的个数,存放到数组num[col]中(num[col]存放原矩阵A中第col列的非零元素个数)
② 计算转置矩阵的每一行在其三元组表中的开始位置,并存放到position[col]中(position[col]存放转置矩阵中第col行的开始位置)
③ 再次扫描原矩阵A的三元组表,根据非零元素的列号col,确定它转置后的行号,查position表,按查到的位置直接将该项存入转置三元组中,并修改position[col],将其指向该行下一个元素的存储位置(position[col]++)。
void Fast_TransposeTSMatrix (TSMatrix *A, TSMatrix *B)
{
int col, t, p;
int num[MAXSIZE], position[MAXSIZE];
position[1] = 1;
if (B->nums)
{
for (col = 1; col <= A->cols; col++)
num[col] = 0;
for (t = 1; t <= A->nums; t++)
num[A->data[t].col] ++;
for (col = 2; col <= A->cols; col++)
position[col] = position[col-1] + num[col-1];
for (p = 1; p <= A->nums; p++)
{
col = A->data[p].col;
B->data[position[p]].row = A->data[p].col;
B->data[position[p]].col = A->data[p].row;
B->data[position[p]].value = A->data[p].value;
position[col]++;
}
}
}
总结
按列序递增进行转置就是通过原三元组A中的col数,依次按照1-col的顺序存入到新三元组B中。而一次定位快速转置算法则是事先通过三元组A的列(对应于三元组B的行)找到每个col号中元素的数目和每个col号的起始位置,从而在三元组B中直接定位位置存入。