假设在m*n的矩阵中,有t个元素不为0,令a=t/m*n 称a为矩阵的稀疏因子。通常认为 a<= 0.05时称为稀疏矩阵。
按照压缩存储的概念,只存储稀疏矩阵的非零元。因此,除了存储非零元的值之外,还必须同时几下它所在行和列的位置(i,j)。反之
一个三元组(i,j,aij)唯一确定了矩阵A的一个非零元。因此,稀疏矩阵可由表示非零元的三元组及其行列数唯一确定。
首先是辅助宏的定义:
#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0
#define OVERFLOW -1
#define UNDERFLOW -2
#define MAXSIZE 12500 //假设非零元个数的最大值为12500
#define MAXMN 12500//假设最大列数 12500
稀疏矩阵的三元组顺序表存储表示:
//稀疏矩阵的三元组顺序表存储表示
typedef struct{
int i,j; //该非0元的行下标和列下标
ElemType e;//非0元的值
}Triple;//三元组类型
typedef struct{
Triple data[MAXSIZE+1]; //非零元三元组表,data[0] 未采用
int rpos[MAXMN+1]; //记录各行第一个非0元的位置
int mu,nu,tu;//矩阵的行数 列数 和非零元个数
}RLSMatrix;//行逻辑链接顺序表类型
创建稀疏矩阵M.
Status CreateSMatrix(RLSMatrix &M){
//创建稀疏矩阵M
int i,j,a,flag=0;
M.tu=0;
scanf("%d %d",&M.mu,&M.nu);
if(M.mu<1||M.nu<1)
return ERROR;
for(i=1;i<=M.mu;i++){
M.rpos[i]=0;
for(j=1;j<=M.nu;j++){
scanf("%d",&a);
if(a!=0) {
M.tu++; //数目增加
if(!flag) {//flag==0判断是这一行第一个元素 是就赋给M.rpos 并将flag赋1
M.rpos[i]=M.tu;
flag=1;
}
M.data[M.tu].i=i;
M.data[M.tu].j=j;
M.data[M.tu].e=a;
}
}
flag=0;//下一行flag赋0
}
return OK;
}
销毁稀疏矩阵M.
Status DestroySMatrix(RLSMatrix &M){
//销毁稀疏矩阵M
M.mu=0;
M.nu=0;
M.tu=0;
return OK;
}
输出稀疏矩阵M。
void PrintRLSMatrix(RLSMatrix &M){
//输出稀疏矩阵M
int i,j,k=1;
if(!M.mu||!M.nu||!M.tu)
printf("空矩阵\n");
else{
for(i=1;i<=M.mu;i++){
for(j=1;j<=M.nu;j++){
if(M.data[k].i==i&&M.data[k].j==j) //行数列数相等
printf("%3d",M.data[k++].e);
else
printf(" 0");//否则输出0
}
printf("\n");
}
}
printf("\n");
}
由稀疏矩阵M复制得到T.
Status CopySMatrix(RLSMatrix M,RLSMatrix &T){
//由稀疏矩阵M复制得到T
T=M;
return OK;
}
稀疏矩阵Q=M+N.
Status AddSMatrix(RLSMatrix M,RLSMatrix N,RLSMatrix &Q){
//稀疏矩阵Q=M+N
if(M.mu!=N.mu||M.nu!=N.nu) //M与N 的行数或列数不相等
return ERROR;
int i,j,k1=1,k2=1;//k1 M中元素位置 k2 N中元素位置
Q.tu=0;
Q.mu=M.mu;
Q.nu=N.nu;
for(i=1;i<=M.mu;i++)
for(j=1;j<=M.nu;j++){
if(M.data[k1].i==i&&M.data[k1].j==j){//如果M此位置元素非零
if(N.data[k2].i==i&&N.data[k2].j==j){//如果N此位置元素也非零
if(M.data[k1].e+N.data[k2].e){ //如果两者相加和不为0 赋给Q.data
Q.tu++;
Q.data[Q.tu].i=i;
Q.data[Q.tu].j=j;
Q.data[Q.tu].e=M.data[k1].e+N.data[k2].e;
}
k1++; //下一个非零元素
k2++;
}
else { //N中此位置元素为0
Q.tu++;
Q.data[Q.tu].i=i;
Q.data[Q.tu].j=j;
Q.data[Q.tu].e=M.data[k1++].e; //M中元素赋给Q,data
}
}
else if(N.data[k2].i==i&&N.data[k2].j==j){//如果N中此位置有非零元素 且M中此位置为0
Q.tu++;
Q.data[Q.tu].i=i;
Q.data[Q.tu].j=j;
Q.data[Q.tu].e=N.data[k2++].e; //N中元素赋给Q,data
}
}
return OK;
}
求稀疏矩阵的差Q=M-N.
Status SubtMatrix(RLSMatrix M,RLSMatrix N,RLSMatrix &Q){
//求稀疏矩阵的差Q=M-N
if(M.mu!=N.mu||M.nu!=N.nu)//矩阵的行或列不相等
return ERROR;
int i,j,k1=1,k2=1;//k1 M中元素位置 k2 N中元素位置
Q.tu=0;
Q.mu=M.mu;
Q.nu=N.nu;
for(i=1;i<=M.mu;i++)
for(j=1;j<=M.nu;j++){
if(M.data[k1].i==i&&M.data[k1].j==j){ //如果M此位置元素非零
if(N.data[k2].i==i&&N.data[k2].j==j){//如果N此位置元素也非零
if(M.data[k1].e-N.data[k2].e){//如果两者相加差不为0 赋给Q.data
Q.tu++;
Q.data[Q.tu].i=i;
Q.data[Q.tu].j=j;
Q.data[Q.tu].e=M.data[k1].e-N.data[k2].e;
}
k1++;//下一个非零元素
k2++;
}
else{//N中此位置元素为0
Q.tu++;
Q.data[Q.tu].i=i;
Q.data[Q.tu].j=j;
Q.data[Q.tu].e=M.data[k1++].e; //M中元素赋给Q,data
}
}
else if(N.data[k2].i==i&&N.data[k2].j==j){//如果N中此位置有非零元素 且M中此位置为0
Q.tu++;
Q.data[Q.tu].i=i;//N中元素赋给Q,data
Q.data[Q.tu].j=j;
Q.data[Q.tu].e=N.data[k2++].e*-1;
}
}
return OK;
}
求稀疏矩阵的转置矩阵T.
列号col 从1变到M.tu 扫描M.data每个元素 对于列号等于j的三元组,将其行标列标互换后依次放入T.data中.
Status TransposeSMatrix(RLSMatrix M,RLSMatrix &T)
{
/*求稀疏矩阵的转置矩阵T
列号col 从1变到M.tu 扫描M.data每个元素 对于列号等于j的三元组
,将其行标列标互换后依次放入T.data中*/
T.mu=M.nu;
T.nu=M.mu;
T.tu=M.tu;
if(T.tu)//如果不是空矩阵
{
int j,k=1,q=1;//q对目标三元组顺序表当前元素计数
for(j=1;j<=M.nu;j++)
for(k=1;k<=M.tu;k++)
if(M.data[k].j==j){//如果当前元素列数等于j M的元素赋给T.data 并把i,j互换
T.data[q].e=M.data[k].e;
T.data[q].i=M.data[k].j;
T.data[q].j=M.data[k].i;
q++;
}
}
return OK;
}
复杂度O(M.nu*M.tu),当足够稀疏时算法更有效,否则最坏O(M.nu^2*M.mu)。
关键原因在于要重复遍历多次顺序表,能否先遍历一次,求出A的元素在B中应该有的位置,之后直接放入呢?
快速求稀疏矩阵的转置矩阵T:
确定M中的每一列的非零元素的个数 计入数组num M中第col列的第一个元素在T中的位置cpot[col]满足 cpot[col]=cpot[col-1]+num[col-1] cpot[1]=1 用p遍历M 第一次遇到列标为col的元素 放入T中第cpot[col] 个位置 没插入一个cpot[col]加一 后遇到clo直接插入cpot[col]。
Status FastTransposeSMatrix(RLSMatrix M,RLSMatrix &T)
{
/*快速求稀疏矩阵的转置矩阵T
确定M中的每一列的非零元素的个数 计入数组num
M中第col列的第一个元素在T中的位置cpot[col]满足 cpot[col]=cpot[col-1]+num[col-1] cpot[1]=1
用p遍历M 第一次遇到列标为col的元素 放入T中第cpot[col] 个位置 没插入一个cpot[col]加一 后遇到clo直接插入cpot[col]*/
T.mu=M.nu;
T.nu=M.mu;
T.tu=M.tu;
if(T.tu){//不是空矩阵
int col,t,q;//col 列号 t 元素位置
int * num=(int *)malloc((M.nu+1)*sizeof(int));//0不用 所以多分配一个
if(!num) //存储分配失败
exit(OVERFLOW);
for(col=1;col<=M.nu;col++)
num[col]=0; //num赋0
for(t=1;t<=M.tu;t++)
num[M.data[t].j]++;
int * cpot=(int *)malloc((M.nu+1)*sizeof(int));
if(!cpot)//存储分配失败
exit(OVERFLOW);
cpot[1]=1;//M中第一个列标为1的非零元素必在T的第一位置
for(col=2;col<=M.nu;col++)
cpot[col]=cpot[col-1]+num[col-1];
for(t=1;t<=M.tu;t++) {
col=M.data[t].j;//求列号
q=cpot[col];//求插入位置
T.data[q].e=M.data[t].e;
T.data[q].j=M.data[t].i;
T.data[q].i=M.data[t].j;
cpot[col]++;//插入位置自增
}
free(num);//销毁清空
free(cpot);
num=NULL;
cpot=NULL;
}
return OK;
}
复杂度:O(M.nu+M.tu) 空间多了num[M.nu+1] cpot[M.nu+1] ,O(M.nu)
给定下标 求元素的指定值:
ElemType Value(RLSMatrix M,int r,int c){
//给定下标 求元素的指定值
int k=M.rpos[r]; //r行第一个非零元素的位置
while(M.data[k].j
求稀疏矩阵乘积Q=M*N。
遍历M中当前行非零元M.data[rpos[Mrow]..rpos[Mrow+1]-1],设在j列遍历N中第j行非零元N.data[rpos[j]...rpos[j+1]-1],设坐标为[j][col]计算M.data[Mrow][j]*N.data[j][col]累计至Qtemp[col]
Status MultSMatrix(RLSMatrix M,RLSMatrix N,RLSMatrix &Q){
//求稀疏矩阵乘积Q=M*N
if(M.nu!=N.mu) //M列数不等于N的行数
return ERROR;
Q.mu=M.mu;//Q初始化
Q.nu=N.nu;
Q.tu=0;
int *ctrmp=(int *)malloc((N.nu+1)*sizeof(int));
if(!ctrmp) //存储分配失败
exit(OVERFLOW);
if(M.tu*N.tu!=0) {//Q是非零矩阵
int arow,blow,tp,p,t,q,i,ccol,j;
for(arow=1;arow<=M.mu;arow++){//逐行求积
for(i=1;i<=N.nu;i++)
ctrmp[i]=0;//Q中各行累加器归零
Q.rpos[arow]=Q.tu+1; //Q的rpos赋值
for(j=arow;jMAXSIZE)
return ERROR; //超出最大容量
Q.data[Q.tu].e=ctrmp[ccol];
Q.data[Q.tu].i=arow;
Q.data[Q.tu].j=ccol;
}
}//for arow
}//if
return OK;
}