// c5-2.h 稀疏矩阵的三元组顺序表存储表示(见图5.4)
#define MAX_SIZE 100 // 非零元个数的最大值
struct Triple
{
int i,j; // 行下标,列下标
ElemType e; // 非零元素值
};
struct TSMatrix
{
Triple data[MAX_SIZE+1]; // 非零元三元组表,data[0]未用
int mu,nu,tu; // 矩阵的行数、列数和非零元个数
};
图55 是采用三元组顺序表存储稀疏矩阵的例子。为简化算法,在创建稀疏矩阵输
入非零元时,要按行、列的顺序由小到大输入。
// bo5-2.cpp 三元组稀疏矩阵的基本操作(8个),包括算法5.1
Status CreateSMatrix(TSMatrix &M)
{ // 创建稀疏矩阵M
int i,m,n;
ElemType e;
Status k;
printf("请输入矩阵的行数,列数,非零元素数:");
scanf("%d,%d,%d",&M.mu,&M.nu,&M.tu);
if(M.tu>MAX_SIZE)
return ERROR;
M.data[0].i=0; // 为以下比较顺序做准备
for(i=1;i<=M.tu;i++)
{
do
{
printf("请按行序顺序输入第%d个非零元素所在的行(1~%d),列(1~%d),元素值:",i,M.mu,M.nu);
scanf("%d,%d,%d",&m,&n,&e);
k=0;
if(m<1||m>M.mu||n<1||n>M.nu) // 行或列超出范围
k=1;
if(m<M.data[i-1].i||m==M.data[i-1].i&&n<=M.data[i-1].j) // 行或列的顺序有错
k=1;
}while(k);
M.data[i].i=m;
M.data[i].j=n;
M.data[i].e=e;
}
return OK;
}
void DestroySMatrix(TSMatrix &M)
{ // 销毁稀疏矩阵M(见图5.6)
M.mu=M.nu=M.tu=0;
}
void PrintSMatrix(TSMatrix M)
{ // 输出稀疏矩阵M
int i;
printf("%d行%d列%d个非零元素。\n",M.mu,M.nu,M.tu);
printf("行列元素值\n");
for(i=1;i<=M.tu;i++)
printf("%2d%4d%8d\n",M.data[i].i,M.data[i].j,M.data[i].e);
}
void PrintSMatrix1(TSMatrix M)
{ // 按矩阵形式输出M
int i,j,k=1;
Triple *p=M.data;
p++; // p指向第1个非零元素
for(i=1;i<=M.mu;i++)
{
for(j=1;j<=M.nu;j++)
if(k<=M.tu&&p->i==i&&p->j==j) // p指向非零元,且p所指元素为当前处理元素
{
printf("%3d",p->e); // 输出p所指元素的值
p++; // p指向下一个元素
k++; // 计数器+1
}
else // p所指元素不是当前处理元素
printf("%3d",0); // 输出0
printf("\n");
}
}
void CopySMatrix(TSMatrix M,TSMatrix &T)
{ // 由稀疏矩阵M复制得到T
T=M;
}
int comp(int c1,int c2)
{ // AddSMatrix函数要用到,另加
if(c1<c2)
return -1;
if(c1==c2)
return 0;
return 1;
}
Status AddSMatrix(TSMatrix M,TSMatrix N,TSMatrix &Q)
{ // 求稀疏矩阵的和Q=M+N
int m=1,n=1,q=0;
if(M.mu!=N.mu||M.nu!=N.nu) // M、N两稀疏矩阵行或列数不同
return ERROR;
Q.mu=M.mu;
Q.nu=M.nu;
while(m<=M.tu&&n<=N.tu) // 矩阵M和N的元素都没处理完
{
switch(comp(M.data[m].i,N.data[n].i))
{
case -1: Q.data[++q]=M.data[m++]; // 将矩阵M的当前元素值赋给矩阵Q
break;
case 0: switch(comp(M.data[m].j,N.data[n].j)) // M、N矩阵当前元素的行相等,继续比较列
{
case -1: Q.data[++q]=M.data[m++];
break;
case 0: Q.data[++q]=M.data[m++]; // M、N矩阵当前非零元素的行列均相等
Q.data[q].e+=N.data[n++].e; //矩阵M、N的当前元素值求和并赋给矩阵Q
if(Q.data[q].e==0) // 元素值为0,不存入压缩矩阵
q--;
break;
case 1: Q.data[++q]=N.data[n++];
}
break;
case 1: Q.data[++q]=N.data[n++]; // 将矩阵N的当前元素值赋给矩阵Q
}
}
while(m<=M.tu) // 矩阵N的元素全部处理完毕
Q.data[++q]=M.data[m++];
while(n<=N.tu) // 矩阵M的元素全部处理完毕
Q.data[++q]=N.data[n++];
Q.tu=q; // 矩阵Q的非零元素个数
if(q>MAX_SIZE) // 非零元素个数太多
return ERROR;
return OK;
}
Status SubtSMatrix(TSMatrix M,TSMatrix N,TSMatrix &Q)
{ // 求稀疏矩阵的差Q=M-N
int i;
for(i=1;i<=N.tu;i++)
N.data[i].e*=-1;
return AddSMatrix(M,N,Q);
}
void TransposeSMatrix(TSMatrix M,TSMatrix &T)
{ // 求稀疏矩阵M的转置矩阵T。算法5.1改
int p,q,col;
T.mu=M.nu;
T.nu=M.mu;
T.tu=M.tu;
if(T.tu)
{
q=1;
for(col=1;col<=M.nu;++col)
for(p=1;p<=M.tu;++p)
if(M.data[p].j==col)
{
T.data[q].i=M.data[p].j;
T.data[q].j=M.data[p].i;
T.data[q].e=M.data[p].e;
++q;
}
}
}
Status MultSMatrix(TSMatrix M,TSMatrix N,TSMatrix &Q)
{ // 求稀疏矩阵的乘积Q=M×N
int i,j;
ElemType *Nc,*Tc;
TSMatrix T; // 临时矩阵
if(M.nu!=N.mu)
return ERROR;
T.nu=M.mu; // 临时矩阵T是Q的转秩矩阵
T.mu=N.nu;
T.tu=0;
Nc=(ElemType*)malloc((N.mu+1)*sizeof(ElemType));//Nc为矩阵N一列的临时数组(非压缩,[0]不用)
Tc=(ElemType*)malloc((M.nu+1)*sizeof(ElemType));//Tc为矩阵T一行的临时数组(非压缩,[0]不用)
if(!Nc||!Tc) // 创建临时数组不成功
exit(ERROR);
for(i=1;i<=N.nu;i++) // 对于N的每一列
{
for(j=1;j<=N.mu;j++)
Nc[j]=0; // 矩阵Nc的初值为0
for(j=1;j<=M.mu;j++)
Tc[j]=0; // 临时数组Tc的初值为0,[0]不用
for(j=1;j<=N.tu;j++) // 对于N的每一个非零元素
if(N.data[j].j==i) // 属于第i列
Nc[N.data[j].i]=N.data[j].e; // 根据其所在行将其元素值赋给相应的Nc
for(j=1;j<=M.tu;j++) // 对于M的每一个值
Tc[M.data[j].i]+=M.data[j].e*Nc[M.data[j].j]; // Tc中存N的第i列与M相乘的结果
for(j=1;j<=M.mu;j++)
if(Tc[j]!=0)
{
T.data[++T.tu].e=Tc[j];
T.data[T.tu].i=i;
T.data[T.tu].j=j;
}
}
if(T.tu>MAX_SIZE) // 非零元素个数太多
return ERROR;
TransposeSMatrix(T,Q); // 将T的转秩赋给Q
DestroySMatrix(T); // 销毁临时矩阵T
free(Tc); // 释放动态数组Tc和Nc
free(Nc);
return OK;
}
// main5-2.cpp 检验bo5-2.cpp的主程序
#include"c1.h"
typedef int ElemType;
#include"c5-2.h"
#include"bo5-2.cpp"
void main()
{
TSMatrix A,B,C;
printf("创建矩阵A: ");
CreateSMatrix(A);
PrintSMatrix(A);
printf("由矩阵A复制矩阵B:\n");
CopySMatrix(A,B);
PrintSMatrix1(B);
DestroySMatrix(B);
printf("销毁矩阵B后:\n");
PrintSMatrix1(B);
printf("创建矩阵B2:(与矩阵A的行、列数相同,行、列分别为%d,%d)\n",A.mu,A.nu);
CreateSMatrix(B);
PrintSMatrix1(B);
AddSMatrix(A,B,C);
printf("矩阵C1(A+B):\n");
PrintSMatrix1(C);
SubtSMatrix(A,B,C);
printf("矩阵C2(A-B):\n");
PrintSMatrix1(C);
TransposeSMatrix(A,C);
printf("矩阵C3(A的转置):\n");
PrintSMatrix1(C);
printf("创建矩阵A2: ");
CreateSMatrix(A);
PrintSMatrix1(A);
printf("创建矩阵B3:(行数应与矩阵A2的列数相同=%d)\n",A.nu);
CreateSMatrix(B);
PrintSMatrix1(B);
MultSMatrix(A,B,C);
printf("矩阵C5(A×B):\n");
PrintSMatrix1(C);
}
代码的运行结果:
创建矩阵A: 请输入矩阵的行数,列数,非零元素数:3,3,2
请按行序顺序输入第1个非零元素所在的行(1~3),列(1~3),元素值:1,2,1
请按行序顺序输入第2个非零元素所在的行(1~3),列(1~3),元素值:2,2,2
3行3列2个非零元素。(见图57)
行列元素值
1 2 1
2 2 2
由矩阵A复制矩阵B:
0 1 0
0 2 0
0 0 0
销毁矩阵B后:
创建矩阵B2:(与矩阵A的行、列数相同,行、列分别为3,3)
请输入矩阵的行数,列数,非零元素数:3,3,1
请按行序顺序输入第1个非零元素所在的行(1~3),列(1~3),元素值:1,2,1
0 1 0
0 0 0
0 0 0
矩阵C1(A+B):
0 2 0
0 2 0
0 0 0
矩阵C2(A-B):
0 0 0
0 2 0
0 0 0
矩阵C3(A的转置):
0 0 0
1 2 0
0 0 0
创建矩阵A2: 请输入矩阵的行数,列数,非零元素数:2,3,2
请按行序顺序输入第1个非零元素所在的行(1~2),列(1~3),元素值:1,1,1
请按行序顺序输入第2个非零元素所在的行(1~2),列(1~3),元素值:2,3,2
1 0 0
0 0 2
创建矩阵B3:(行数应与矩阵A2的列数相同=3)
请输入矩阵的行数,列数,非零元素数:3,2,2
请按行序顺序输入第1个非零元素所在的行(1~3),列(1~2),元素值:2,2,1
请按行序顺序输入第2个非零元素所在的行(1~3),列(1~2),元素值:3,1,2
0 0
0 1
2 0
矩阵C5(A×B):
0 0
4 0
// algo5-1.cpp 实现算法5.2的程序
#include"c1.h"
typedef int ElemType;
#include"c5-2.h"
#include"bo5-2.cpp"
void FastTransposeSMatrix(TSMatrix M,TSMatrix &T)
{ // 快速求稀疏矩阵M的转置矩阵T。算法5.2改
int p,q,t,col,*num,*cpot;
num=(int *)malloc((M.nu+1)*sizeof(int)); // 存M每列(T每行)非零元素个数([0]不用)
cpot=(int *)malloc((M.nu+1)*sizeof(int)); // 存T每行的下1个非零元素的存储位置([0]不用)
T.mu=M.nu; // 给T的行、列数与非零元素个数赋值
T.nu=M.mu;
T.tu=M.tu;
if(T.tu) // 是非零矩阵
{
for(col=1;col<=M.nu;++col)
num[col]=0; // 计数器初值设为0
for(t=1;t<=M.tu;++t) // 求M中每一列含非零元素个数
++num[M.data[t].j];
cpot[1]=1; // T的第1行的第1个非零元在T.data中的序号为1
for(col=2;col<=M.nu;++col)
cpot[col]=cpot[col-1]+num[col-1]; // 求T的第col行的第1个非零元在T.data中的序号
for(p=1;p<=M.tu;++p) // 从M的第1个元素开始
{
col=M.data[p].j; // 求得在M中的列数
q=cpot[col]; // q指示M当前的元素在T中的序号
T.data[q].i=M.data[p].j;
T.data[q].j=M.data[p].i;
T.data[q].e=M.data[p].e;
++cpot[col]; // T第col行的下1个非零元在T.data中的序号
}
}
free(num);
free(cpot);
}
void main()
{
TSMatrix A,B;
printf("创建矩阵A: ");
CreateSMatrix(A);
PrintSMatrix1(A);
FastTransposeSMatrix(A,B);
printf("矩阵B(A的快速转置):\n");
PrintSMatrix1(B);
}
代码的运行结果:
创建矩阵A: 请输入矩阵的行数,列数,非零元素数:3,2,4
请按行序顺序输入第1个非零元素所在的行(1~3),列(1~2),元素值:1,1,1
请按行序顺序输入第2个非零元素所在的行(1~3),列(1~2),元素值:2,1,2
请按行序顺序输入第3个非零元素所在的行(1~3),列(1~2),元素值:3,1,3
请按行序顺序输入第4个非零元素所在的行(1~3),列(1~2),元素值:3,2,4
1 0
2 0
3 4
矩阵B(A的快速转置):
1 2 3
0 0 4
由于稀疏矩阵的三元组顺序表存储结构要求先按行、同行再按列顺序存储非零元素,
算法5.1(在bo5-2.cpp 中)采用了双重循环求转置矩阵,对于外层循环col(列)的每一个
值,对所有的非零元素,如果其列数与col 相等,则按顺序存入转秩矩阵。这就保证了转
秩矩阵也是先按行、同行再按列顺序存储非零元素。所以它的时间复杂度为O(列数×非
零元素数)。而算法5.2(在algo5-1.cpp 中)采用了2 个单循环。第1 个循环,对所有的非
零元素,计算其所在列并计数,得到每列的非零元素数num[col]及每列第1 个非零元素在
转秩矩阵中的存储位置cpot[col]。第2 个循环,对所有的非零元素,根据其列数和
cpot[col]的当前值,存入转秩矩阵。由于还要给num[col]和cpot[col]赋初值,它的时间复
杂度为O(列数+非零元素数)。
// c5-3.h 稀疏矩阵的三元组行逻辑链接的顺序表存储表示(见图5.8)
#define MAX_SIZE 100 // 非零元个数的最大值
#define MAX_RC 20 // 最大行列数
struct Triple // 同c5-2.h
{
int i,j; // 行下标,列下标
ElemType e; // 非零元素值
};
struct RLSMatrix
{
Triple data[MAX_SIZE+1]; // 非零元三元组表,data[0]未用
int rpos[MAX_RC+1]; // 各行第一个非零元素的位置表,比c5-2.h增加的项
int mu,nu,tu; // 矩阵的行数、列数和非零元个数
};
三元组行逻辑链接的顺序表存储表示(c5-3.h)比三元组顺序表存储表示(c5-2.h)增加
了rpos 数组,用以存放各行的第一个非零元素在data 数组中的位置。这样,就可以迅速地
找到某一行的元素。图59 是采用三元组行逻辑链接的顺序表存储稀疏矩阵的实例。和
c5-2.h 存储结构一样,创建稀疏矩阵输入非零元时,也要按行、列的顺序由小到大输入。
// bo5-3.cpp 行逻辑链接稀疏矩阵(存储结构由c5-3.h定义)的基本操作(8个),包括算法5.3
Status CreateSMatrix(RLSMatrix &M)
{ // 创建稀疏矩阵M
int i,j;
Triple T;
Status k;
printf("请输入矩阵的行数,列数,非零元素数:");
scanf("%d,%d,%d",&M.mu,&M.nu,&M.tu);
if(M.tu>MAX_SIZE||M.mu>MAX_RC)
return ERROR;
M.data[0].i=0; // 为以下比较做准备
for(i=1;i<=M.tu;i++)
{
do
{
printf("请按行序顺序输入第%d个非零元素所在的行(1~%d),列(1~%d),元素值:",i,M.mu,M.nu);
scanf("%d,%d,%d",&T.i,&T.j,&T.e);
k=0;
if(T.i<1||T.i>M.mu||T.j<1||T.j>M.nu) // 行、列超出范围
k=1;
if(T.i<M.data[i-1].i||T.i==M.data[i-1].i&&T.j<=M.data[i-1].j)//没有按顺序输入非零元素
k=1;
}while(k); // 当输入有误,重新输入
M.data[i]=T;
}
for(i=1;i<=M.mu;i++) // 给rpos[]赋初值0
M.rpos[i]=0;
for(i=1;i<=M.tu;i++) // 计算每行非零元素数并赋给rpos[]
M.rpos[M.data[i].i]++;
for(i=M.mu;i>=1;i--) // 计算rpos[]
{
M.rpos[i]=1; // 赋初值1
for(j=i-1;j>=1;j--)
M.rpos[i]+=M.rpos[j];
}
return OK;
}
void DestroySMatrix(RLSMatrix &M)
{ // 销毁稀疏矩阵M(使M为0行0列0个非零元素的矩阵)
M.mu=M.nu=M.tu=0;
}
void PrintSMatrix(RLSMatrix M)
{ // 输出稀疏矩阵M
int i;
printf("%d行%d列%d个非零元素。\n",M.mu,M.nu,M.tu);
printf("行列元素值\n");
for(i=1;i<=M.tu;i++)
printf("%2d%4d%8d\n",M.data[i].i,M.data[i].j,M.data[i].e);
for(i=1;i<=M.mu;i++)
printf("第%d行的第一个非零元素是本矩阵第%d个元素\n",i,M.rpos[i]);
}
void PrintSMatrix1(RLSMatrix M)
{ // 按矩阵形式输出M
int i,j,k=1;
Triple *p=M.data;
p++; // p指向第1个非零元素
for(i=1;i<=M.mu;i++)
{
for(j=1;j<=M.nu;j++)
if(k<=M.tu&&p->i==i&&p->j==j) // p指向非零元,且p所指元素为当前处理元素
{
printf("%3d",p->e); // 输出p所指元素的值
p++; // p指向下一个元素
k++; // 计数器+1
}
else // p所指元素不是当前处理元素
printf("%3d",0); // 输出0
printf("\n");
}
}
void CopySMatrix(RLSMatrix M,RLSMatrix &T)
{ // 由稀疏矩阵M复制得到T
T=M;
}
Status AddSMatrix(RLSMatrix M,RLSMatrix N,RLSMatrix &Q)
{ // 求稀疏矩阵的和Q=M+N
int k,p,q,tm,tn;
if(M.mu!=N.mu||M.nu!=N.nu)
return ERROR;
Q.mu=M.mu; // Q矩阵行数
Q.nu=M.nu; // Q矩阵列数
Q.tu=0; // Q矩阵非零元素数初值
for(k=1;k<=M.mu;++k) // 对于每一行,k指示行号
{
Q.rpos[k]=Q.tu+1; // Q矩阵第k行的第1个元素的位置
p=M.rpos[k]; // p指示M矩阵第k行当前元素的序号
q=N.rpos[k]; // q指示N矩阵第k行当前元素的序号
if(k==M.mu) // 是最后一行
{
tm=M.tu+1; // tm,tn分别是p,q的上界
tn=N.tu+1;
}
else
{
tm=M.rpos[k+1];
tn=N.rpos[k+1];
}
while(p<tm&&q<tn) // M,N矩阵均有第k行元素未处理
if(M.data[p].j==N.data[q].j) // M矩阵当前元素的列=N矩阵当前元素的列
{
if(M.data[p].e+N.data[q].e!=0) // 和不为0,存入Q
{
Q.data[++Q.tu]=M.data[p];
Q.data[Q.tu].e+=N.data[q].e;
}
p++;
q++;
}
else if(M.data[p].j<N.data[q].j) // M矩阵当前元素的列<N矩阵当前元素的列
Q.data[++Q.tu]=M.data[p++]; // 将M的当前值赋给Q
else // M矩阵当前元素的列>N矩阵当前元素的列
Q.data[++Q.tu]=N.data[q++]; // 将N的当前值赋给Q
while(p<tm) // M矩阵还有第k行的元素未处理
Q.data[++Q.tu]=M.data[p++]; // 将M的当前值赋给Q
while(q<tn) // N矩阵还有k行的元素未处理
Q.data[++Q.tu]=N.data[q++]; // 将N的当前值赋给Q
}
if(Q.tu>MAX_SIZE)
return ERROR;
else
return OK;
}
Status SubtSMatrix(RLSMatrix M,RLSMatrix N,RLSMatrix &Q)
{ // 求稀疏矩阵的差Q=M-N
int i;
if(M.mu!=N.mu||M.nu!=N.nu)
return ERROR;
for(i=1;i<=N.tu;++i) // 对于N的每一元素,其值乘以-1
N.data[i].e*=-1;
AddSMatrix(M,N,Q); // Q=M+(-N)
return OK;
}
Status MultSMatrix(RLSMatrix M,RLSMatrix N,RLSMatrix &Q)
{ // 求稀疏矩阵乘积Q=M×N。算法5.3改
int arow,brow,p,q,ccol,ctemp[MAX_RC+1],t,tp;
if(M.nu!=N.mu) // 矩阵M的列数应和矩阵N的行数相等
return ERROR;
Q.mu=M.mu; // Q初始化
Q.nu=N.nu;
Q.tu=0;
if(M.tu*N.tu==0) // M和N至少有一个是零矩阵
return ERROR;
for(arow=1;arow<=M.mu;++arow)
{ // 从M的第一行开始,到最后一行,arow是M的当前行
for(ccol=1;ccol<=Q.nu;++ccol)
ctemp[ccol]=0; // Q的当前行的各列元素累加器清零
Q.rpos[arow]=Q.tu+1; // Q当前行的第1个元素位于上1行最后1个元素之后
if(arow<M.mu)
tp=M.rpos[arow+1];
else
tp=M.tu+1; // 给最后1行设界
for(p=M.rpos[arow];p<tp;++p)
{ // 对M当前行中每一个非零元
brow=M.data[p].j; // 找到对应元在N中的行号(M当前元的列号)
if(brow<N.mu)
t=N.rpos[brow+1];
else
t=N.tu+1; // 给最后1行设界
for(q=N.rpos[brow];q<t;++q)
{
ccol=N.data[q].j; // 乘积元素在Q中列号
ctemp[ccol]+=M.data[p].e*N.data[q].e;
}
} // 求得Q中第arow行的非零元
for(ccol=1;ccol<=Q.nu;++ccol) // 压缩存储该行非零元
if(ctemp[ccol]!=0)
{
if(++Q.tu>MAX_SIZE)
return ERROR;
Q.data[Q.tu].i=arow;
Q.data[Q.tu].j=ccol;
Q.data[Q.tu].e=ctemp[ccol];
}
}
return OK;
}
void TransposeSMatrix(RLSMatrix M,RLSMatrix &T)
{ // 求稀疏矩阵M的转置矩阵T
int p,q,t,col,*num;
num=(int *)malloc((M.nu+1)*sizeof(int));
T.mu=M.nu;
T.nu=M.mu;
T.tu=M.tu;
if(T.tu)
{
for(col=1;col<=M.nu;++col)
num[col]=0; // 设初值
for(t=1;t<=M.tu;++t) // 求M中每一列非零元个数
++num[M.data[t].j];
T.rpos[1]=1;
for(col=2;col<=M.nu;++col) // 求M中第col中第一个非零元在T.data中的序号
T.rpos[col]=T.rpos[col-1]+num[col-1];
for(col=1;col<=M.nu;++col)
num[col]=T.rpos[col];
for(p=1;p<=M.tu;++p)
{
col=M.data[p].j;
q=num[col];
T.data[q].i=M.data[p].j;
T.data[q].j=M.data[p].i;
T.data[q].e=M.data[p].e;
++num[col];
}
}
free(num);
}
// main5-3.cpp 检验bo5-3.cpp的主程序(与main5-2.cpp很相像)
#include"c1.h"
typedef int ElemType;
#include"c5-3.h" // 此行与main5-2.cpp不同
#include"bo5-3.cpp" // 此行与main5-2.cpp不同
void main()
{
RLSMatrix A,B,C; // 此行与main5-2.cpp不同
printf("创建矩阵A: ");
CreateSMatrix(A);
PrintSMatrix(A);
printf("由矩阵A复制矩阵B:\n");
CopySMatrix(A,B);
PrintSMatrix1(B);
DestroySMatrix(B);
printf("销毁矩阵B后:\n");
PrintSMatrix1(B);
printf("创建矩阵B2:(与矩阵A的行、列数相同,行、列分别为%d,%d)\n",A.mu,A.nu);
CreateSMatrix(B);
PrintSMatrix1(B);
AddSMatrix(A,B,C);
printf("矩阵C1(A+B):\n");
PrintSMatrix1(C);
SubtSMatrix(A,B,C);
printf("矩阵C2(A-B):\n");
PrintSMatrix1(C);
TransposeSMatrix(A,C);
printf("矩阵C3(A的转置):\n");
PrintSMatrix1(C);
printf("创建矩阵A2:\n");
CreateSMatrix(A);
PrintSMatrix1(A);
printf("创建矩阵B3:(行数应与矩阵A2的列数相同=%d)\n",A.nu);
CreateSMatrix(B);
PrintSMatrix1(B);
MultSMatrix(A,B,C);
printf("矩阵C5(A×B):\n");
PrintSMatrix1(C);
}
代码的运行结果:
创建矩阵A: 请输入矩阵的行数,列数,非零元素数:3,3,2
请按行序顺序输入第1个非零元素所在的行(1~3),列(1~3),元素值:1,2,1
请按行序顺序输入第2个非零元素所在的行(1~3),列(1~3),元素值:2,2,2
3行3列2个非零元素。(见图57)
行列元素值
1 2 1
2 2 2
第1行的第一个非零元素是本矩阵第1个元素
第2行的第一个非零元素是本矩阵第2个元素
第3行的第一个非零元素是本矩阵第3个元素
由矩阵A复制矩阵B:
0 1 0
0 2 0
0 0 0
销毁矩阵B后:
创建矩阵B2:(与矩阵A的行、列数相同,行、列分别为3,3)
请输入矩阵的行数,列数,非零元素数:3,3,1
请按行序顺序输入第1个非零元素所在的行(1~3),列(1~3),元素值:1,2,1
0 1 0
0 0 0
0 0 0
矩阵C1(A+B):
0 2 0
0 2 0
0 0 0
矩阵C2(A-B):
0 0 0
0 2 0
0 0 0
矩阵C3(A的转置):
0 0 0
1 2 0
0 0 0
创建矩阵A2:
请输入矩阵的行数,列数,非零元素数:2,3,2
请按行序顺序输入第1个非零元素所在的行(1~2),列(1~3),元素值:1,1,1
请按行序顺序输入第2个非零元素所在的行(1~2),列(1~3),元素值:2,3,2
1 0 0
0 0 2
创建矩阵B3:(行数应与矩阵A2的列数相同=3)
请输入矩阵的行数,列数,非零元素数:3,2,2
请按行序顺序输入第1个非零元素所在的行(1~3),列(1~2),元素值:2,2,1
请按行序顺序输入第2个非零元素所在的行(1~3),列(1~2),元素值:3,1,2
0 0
0 1
2 0
矩阵C5(A×B):
0 0
4 0
// c5-4.h 稀疏矩阵的十字链表存储表示(见图5.10)
struct OLNode
{
int i,j; // 该非零元的行和列下标
ElemType e; // 非零元素值
OLNode *right,*down;
// 该非零元所在行表和列表的后继链域
};
typedef OLNode *OLink;
struct CrossList
{
OLink *rhead,*chead;
// 行和列链表头指针向量基址,由CreatSMatrix_OL()分配
int mu,nu,tu; // 稀疏矩阵的行数、列数和非零元个数
};
图511 是采用十字链表存储稀疏矩阵的实例(教科书图5.6)。由于十字链表存储结
构中的非零元素是按其所在行、列插入相应的链表的,所以,在创建稀疏矩阵输入非零元
时,可以按任意顺序输入非零元素。每个非零元结点按升序被插入到两个没有头结点的单
链表中:一个是所在行链表;另一个是所在列链表。当插入或删除结点时,只要修改相关的行、列链表即可,比较灵活。
// bo5-4.cpp 稀疏矩阵的十字链表存储(存储结构由c5-4.h定义)的基本操作(9个),包括算法5.4
void InitSMatrix(CrossList &M)
{ // 初始化M(CrossList类型的变量必须初始化,否则创建、复制矩阵将出错)。加(见图5.12)
M.rhead=M.chead=NULL;
M.mu=M.nu=M.tu=0;
}
void InitSMatrixList(CrossList &M)
{ // 初始化十字链表表头指针向量。加(见图5.13)
int i;
if(!(M.rhead=(OLink*)malloc((M.mu+1)*sizeof(OLink))))
// 生成行表头指针向量
exit(OVERFLOW);
if(!(M.chead=(OLink*)malloc((M.nu+1)*sizeof(OLink))))
// 生成列表头指针向量
exit(OVERFLOW);
for(i=1;i<=M.mu;i++)
// 初始化矩阵T的行表头指针向量,各行链表为空
M.rhead[i]=NULL;
for(i=1;i<=M.nu;i++)
// 初始化矩阵T的列表头指针向量,各列链表为空
M.chead[i]=NULL;
}
void InsertAscend(CrossList &M,OLink p)
{ // 初始条件:稀疏矩阵M存在,p指向的结点存在。
// 操作结果:按行列升序将p所指结点插入M(见图5.14)
OLink q=M.rhead[p->i]; // q指向待插行表
if(!q||p->j<q->j) // 待插的行表空或p所指结点的列值小于首结点的列值
{
p->right=M.rhead[p->i]; // 插在表头
M.rhead[p->i]=p;
}
else
{
while(q->right&&q->right->j<p->j)//q所指不是尾结点且q的下一结点的列值小于p所指结点的列值
q=q->right; // q向后移
p->right=q->right; // 将p插在q所指结点之后
q->right=p;
}
q=M.chead[p->j]; // q指向待插列表
if(!q||p->i<q->i) // 待插的列表空或p所指结点的行值小于首结点的行值
{
p->down=M.chead[p->j]; // 插在表头
M.chead[p->j]=p;
}
else
{
while(q->down&&q->down->i<p->i) //q所指不是尾结点且q的下一结点的行值小于p所指结点的行值
q=q->down; // q向下移
p->down=q->down; // 将p插在q所指结点之下
q->down=p;
}
M.tu++;
}
void DestroySMatrix(CrossList &M)
{ // 初始条件:稀疏矩阵M存在。操作结果:销毁稀疏矩阵M(见图5.12)
int i;
OLink p,q;
for(i=1;i<=M.mu;i++) // 按行释放结点
{
p=*(M.rhead+i);
while(p)
{
q=p;
p=p->right;
free(q);
}
}
free(M.rhead);
free(M.chead);
InitSMatrix(M);
}
void CreateSMatrix(CrossList &M)
{ // 创建稀疏矩阵M,采用十字链表存储表示。算法5.4改
int i,k;
OLink p;
if(M.rhead)
DestroySMatrix(M);
printf("请输入稀疏矩阵的行数列数非零元个数: ");
scanf("%d%d%d",&M.mu,&M.nu,&i);
InitSMatrixList(M); // 初始化M的表头指针向量
printf("请按任意次序输入%d个非零元的行列元素值:\n",M.tu);
for(k=0;k<i;k++)
{
p=(OLink)malloc(sizeof(OLNode)); // 生成结点
if(!p)
exit(OVERFLOW);
scanf("%d%d%d",&p->i,&p->j,&p->e); // 给结点的3个成员赋值
InsertAscend(M,p); // 将结点p按行列值升序插到矩阵M中
}
}
void PrintSMatrix(CrossList M)
{ // 初始条件:稀疏矩阵M存在。操作结果:按行或按列输出稀疏矩阵M
int i,j;
OLink p;
printf("%d行%d列%d个非零元素\n",M.mu,M.nu,M.tu);
printf("请输入选择(1.按行输出2.按列输出): ");
scanf("%d",&i);
switch(i)
{
case 1: for(j=1;j<=M.mu;j++)
{
p=M.rhead[j];
while(p)
{
cout<<p->i<<"行"<<p->j<<"列值为"<<p->e<<endl;
p=p->right;
}
}
break;
case 2: for(j=1;j<=M.nu;j++)
{
p=M.chead[j];
while(p)
{
cout<<p->i<<"行"<<p->j<<"列值为"<<p->e<<endl;
p=p->down;
}
}
}
}
void PrintSMatrix1(CrossList M)
{ // 按矩阵形式输出M
int i,j;
OLink p;
for(i=1;i<=M.mu;i++)
{ // 从第1行到最后1行
p=M.rhead[i]; // p指向该行的第1个非零元素
for(j=1;j<=M.nu;j++) // 从第1列到最后1列
if(!p||p->j!=j) // 已到该行表尾或当前结点的列值不等于当前列值
printf("%-3d",0); // 输出0
else
{
printf("%-3d",p->e);
p=p->right;
}
printf("\n");
}
}
void CopySMatrix(CrossList M,CrossList &T)
{ // 初始条件:稀疏矩阵M存在。操作结果:由稀疏矩阵M复制得到T
int i;
OLink p,q;
if(T.rhead) // 矩阵T存在
DestroySMatrix(T);
T.mu=M.mu;
T.nu=M.nu;
InitSMatrixList(T); // 初始化T的表头指针向量
for(i=1;i<=M.mu;i++) // 按行复制
{
p=M.rhead[i]; // p指向i行链表头
while(p) // 没到行尾
{
if(!(q=(OLNode*)malloc(sizeof(OLNode)))) // 生成结点q
exit(OVERFLOW);
*q=*p; // 给结点q赋值
InsertAscend(T,q); // 将结点q按行列值升序插到矩阵T中
p=p->right;
}
}
}
int comp(int c1,int c2)
{ // AddSMatrix函数要用到,另加
if(c1<c2)
return -1;
if(c1==c2)
return 0;
return 1;
}
void AddSMatrix(CrossList M,CrossList N,CrossList &Q)
{ // 初始条件:稀疏矩阵M与N的行数和列数对应相等。操作结果:求稀疏矩阵的和Q=M+N
int i;
OLink pq,pm,pn;
if(M.mu!=N.mu||M.nu!=N.nu)
{
printf("两个矩阵不是同类型的,不能相加\n");
exit(OVERFLOW);
}
Q.mu=M.mu; // 初始化Q矩阵
Q.nu=M.nu;
Q.tu=0; // Q矩阵元素个数的初值为0
InitSMatrixList(Q); // 初始化Q的表头指针向量
for(i=1;i<=M.mu;i++) // 按行的顺序相加
{
pm=M.rhead[i]; // pm指向矩阵M的第i行的第1个结点
pn=N.rhead[i]; // pn指向矩阵N的第i行的第1个结点
while(pm&&pn) // pm和pn均不空
{
pq=(OLink)malloc(sizeof(OLNode)); // 生成矩阵Q的结点
switch(comp(pm->j,pn->j))
{
case -1: *pq=*pm; // M的列<N的列,将矩阵M的当前元素值赋给pq
InsertAscend(Q,pq); // 将结点pq按行列值升序插到矩阵Q中
pm=pm->right; // 指针向后移
break;
case 0: *pq=*pm; // M、N矩阵的列相等,元素值相加
pq->e+=pn->e;
if(pq->e!=0) // 和为非零元素
InsertAscend(Q,pq); // 将结点pq按行列值升序插到矩阵Q中
else
free(pq); // 释放结点
pm=pm->right; // 指针向后移
pn=pn->right;
break;
case 1: *pq=*pn; // M的列>N的列,将矩阵N的当前元素值赋给pq
InsertAscend(Q,pq); // 将结点pq按行列值升序插到矩阵Q中
pn=pn->right; // 指针向后移
}
}
while(pm) // pn=NULL
{
pq=(OLink)malloc(sizeof(OLNode)); // 生成矩阵Q的结点
*pq=*pm; // M的列<N的列,将矩阵M的当前元素值赋给pq
InsertAscend(Q,pq); // 将结点pq按行列值升序插到矩阵Q中
pm=pm->right; // 指针向后移
}
while(pn) // pm=NULL
{
pq=(OLink)malloc(sizeof(OLNode)); // 生成矩阵Q的结点
*pq=*pn; // M的列>N的列,将矩阵N的当前元素值赋给pq
InsertAscend(Q,pq); // 将结点pq按行列值升序插到矩阵Q中
pn=pn->right; // 指针向后移
}
}
if(Q.tu==0) // Q矩阵元素个数为0
DestroySMatrix(Q); // 销毁矩阵Q
}
void SubtSMatrix(CrossList M,CrossList N,CrossList &Q)
{ // 初始条件:稀疏矩阵M与N的行数和列数对应相等。操作结果:求稀疏矩阵的差Q=M-N
int i;
OLink pq,pm,pn;
if(M.mu!=N.mu||M.nu!=N.nu)
{
printf("两个矩阵不是同类型的,不能相减\n");
exit(OVERFLOW);
}
Q.mu=M.mu; // 初始化Q矩阵
Q.nu=M.nu;
Q.tu=0; // Q矩阵元素个数的初值为0
InitSMatrixList(Q); // 初始化Q的表头指针向量
for(i=1;i<=M.mu;i++) // 按行的顺序相减
{
pm=M.rhead[i]; // pm指向矩阵M的第i行的第1个结点
pn=N.rhead[i]; // pn指向矩阵N的第i行的第1个结点
while(pm&&pn) // pm和pn均不空
{
pq=(OLink)malloc(sizeof(OLNode)); // 生成矩阵Q的结点
switch(comp(pm->j,pn->j))
{
case -1: *pq=*pm; // M的列<N的列,将矩阵M的当前元素值赋给pq
InsertAscend(Q,pq); // 将结点pq按行列值升序插到矩阵Q中
pm=pm->right; // 指针向后移
break;
case 0: *pq=*pm; // M、N矩阵的列相等,元素值相减
pq->e-=pn->e;
if(pq->e!=0) // 差为非零元素
InsertAscend(Q,pq); // 将结点pq按行列值升序插到矩阵Q中
else
free(pq); // 释放结点
pm=pm->right; // 指针向后移
pn=pn->right;
break;
case 1: *pq=*pn; // M的列>N的列,将矩阵N的当前元素值赋给pq
pq->e*=-1; // 求反
InsertAscend(Q,pq); // 将结点pq按行列值升序插到矩阵Q中
pn=pn->right; // 指针向后移
}
}
while(pm) // pn=NULL
{
pq=(OLink)malloc(sizeof(OLNode)); // 生成矩阵Q的结点
*pq=*pm; // M的列<N的列,将矩阵M的当前元素值赋给pq
InsertAscend(Q,pq); // 将结点pq按行列值升序插到矩阵Q中
pm=pm->right; // 指针向后移
}
while(pn) // pm=NULL
{
pq=(OLink)malloc(sizeof(OLNode)); // 生成矩阵Q的结点
*pq=*pn; // M的列>N的列,将矩阵N的当前元素值赋给pq
pq->e*=-1; // 求反
InsertAscend(Q,pq); // 将结点pq按行列值升序插到矩阵Q中
pn=pn->right; // 指针向后移
}
}
if(Q.tu==0) // Q矩阵元素个数为0
DestroySMatrix(Q); // 销毁矩阵Q
}
void MultSMatrix(CrossList M,CrossList N,CrossList &Q)
{ // 初始条件:稀疏矩阵M的列数等于N的行数。操作结果:求稀疏矩阵乘积Q=M×N
int i,j,e;
OLink pq,pm,pn;
InitSMatrix(Q);
Q.mu=M.mu;
Q.nu=N.nu;
Q.tu=0;
InitSMatrixList(Q); // 初始化Q的表头指针向量
for(i=1;i<=Q.mu;i++)
for(j=1;j<=Q.nu;j++)
{
pm=M.rhead[i];
pn=N.chead[j];
e=0;
while(pm&&pn)
switch(comp(pn->i,pm->j))
{
case -1: pn=pn->down; // 列指针后移
break;
case 0: e+=pm->e*pn->e; // 乘积累加
pn=pn->down; // 行列指针均后移
pm=pm->right;
break;
case 1: pm=pm->right; // 行指针后移
}
if(e) // 值不为0
{
pq=(OLink)malloc(sizeof(OLNode)); // 生成结点
if(!pq) // 生成结点失败
exit(OVERFLOW);
pq->i=i; // 给结点赋值
pq->j=j;
pq->e=e;
InsertAscend(Q,pq); // 将结点pq按行列值升序插到矩阵Q中
}
}
if(Q.tu==0) // Q矩阵元素个数为0
DestroySMatrix(Q); // 销毁矩阵Q
}
void TransposeSMatrix(CrossList M,CrossList &T)
{ // 初始条件:稀疏矩阵M存在。操作结果:求稀疏矩阵M的转置矩阵T
int u,i;
OLink *head,p,q,r;
CopySMatrix(M,T); // T=M
u=T.mu; // 交换T.mu和T.nu
T.mu=T.nu;
T.nu=u;
head=T.rhead; // 交换T.rhead和T.chead
T.rhead=T.chead;
T.chead=head;
for(u=1;u<=T.mu;u++) // 对T的每一行
{
p=T.rhead[u]; // p为行表头
while(p) // 没到表尾,对T的每一结点
{
q=p->down; // q指向下一个结点
i=p->i; // 交换.i和.j
p->i=p->j;
p->j=i;
r=p->down; // 交换.down和.right
p->down=p->right;
p->right=r;
p=q; // p指向下一个结点
}
}
}
// main5-4.cpp 检验bo5-4.cpp的主程序
#include"c1.h"
typedef int ElemType;
#include"c5-4.h"
#include"bo5-4.cpp"
void main()
{
CrossList A,B,C;
InitSMatrix(A); // CrossList类型的变量在初次使用之前必须初始化
InitSMatrix(B);
printf("创建矩阵A: ");
CreateSMatrix(A);
PrintSMatrix(A);
printf("由矩阵A复制矩阵B: ");
CopySMatrix(A,B);
PrintSMatrix(B);
DestroySMatrix(B); // CrossList类型的变量在再次使用之前必须先销毁
printf("销毁矩阵B后,矩阵B为\n");
PrintSMatrix1(B);
printf("创建矩阵B2:(与矩阵A的行、列数相同,行、列分别为%d,%d)\n",A.mu,A.nu);
CreateSMatrix(B);
PrintSMatrix1(B);
printf("矩阵C1(A+B):\n");
AddSMatrix(A,B,C);
PrintSMatrix1(C);
DestroySMatrix(C);
printf("矩阵C2(A-B):\n");
SubtSMatrix(A,B,C);
PrintSMatrix1(C);
DestroySMatrix(C);
printf("矩阵C3(A的转置):\n");
TransposeSMatrix(A,C);
PrintSMatrix1(C);
DestroySMatrix(A);
DestroySMatrix(B);
DestroySMatrix(C);
printf("创建矩阵A2: ");
CreateSMatrix(A);
PrintSMatrix1(A);
printf("创建矩阵B3:(行数应与矩阵A2的列数相同=%d)\n",A.nu);
CreateSMatrix(B);
PrintSMatrix1(B);
printf("矩阵C5(A×B):\n");
MultSMatrix(A,B,C);
PrintSMatrix1(C);DestroySMatrix(A);
DestroySMatrix(B);
DestroySMatrix(C);
}
代码运行结果:
创建矩阵A: 请输入稀疏矩阵的行数列数非零元个数: 3 3 2
请按任意次序输入2个非零元的行列元素值:
2 1 2
1 2 1
3行3列2个非零元素(见图515)
请输入选择(1.按行输出2.按列输出): 1
1行2列值为1
2行1列值为2
由矩阵A复制矩阵B: 3行3列2个非零元素
请输入选择(1.按行输出2.按列输出): 2
2行1列值为2
1行2列值为1
销毁矩阵B后,矩阵B为
创建矩阵B2:(与矩阵A的行、列数相同,行、列分别为3,3)
请输入稀疏矩阵的行数列数非零元个数: 3 3 2
请按任意次序输入2个非零元的行列元素值:
2 2 5
1 2 -1
0 -1 0
0 5 0
0 0 0
矩阵C1(A+B):
0 0 0
2 5 0
0 0 0
矩阵C2(A-B):
0 2 0
2 -5 0
0 0 0
矩阵C3(A的转置):
0 2 0
1 0 0
0 0 0
创建矩阵A2: 请输入稀疏矩阵的行数列数非零元个数: 2 3 2
请按任意次序输入2个非零元的行列元素值:
1 1 1
2 3 2
1 0 0
0 0 2
创建矩阵B3:(行数应与矩阵A2的列数相同=3)
请输入稀疏矩阵的行数列数非零元个数: 3 2 2
请按任意次序输入2个非零元的行列元素值:
2 2 1
3 1 2
0 0
0 1
2 0
矩阵C5(A×B):
0 0
4 0