题目:设计一个程序,实现一个能进行稀疏矩阵基本运算的计算器。按照教科书《数据结构(C语言版)》(严蔚敏等)5.3.2节中描述的方法,以十字链表表示稀疏矩阵。
一、需求分析
稀疏矩阵是指那些多数元素为零的矩阵。利用“稀疏”特点进修学校储存和计算可以大大节省储存空间,提高计算效率。实现一个能进行稀疏矩阵基本运算的计算器。
以“带行逻辑链接信息”的三元组顺序表表示稀疏矩阵,实现两个矩阵相加、相减和相乘的运算。稀疏矩阵的输入形式采用三元组表示,而运算结果的矩阵则以通常的阵列形式列出。
测试数据
\left[ \begin{matrix} 10&0& 0 \\ 0&0&9 \\ -1&0&0 \\ \end{matrix}\right] + \left[ \begin{matrix} 0&0& 0 \\ 0&0&-1 \\ 1&0&-3 \\ \end{matrix}\right]= \left[ \begin{matrix} 10&0& 0 \\ 0&0&8 \\ 0&0&-3 \\ \end{matrix}\right]
\left[ \begin{matrix} 10&9 \\ 0&9 \\ -1&0 \\ \end{matrix}\right] - \left[ \begin{matrix} 0&0 \\ 0&-1 \\ 1&-3 \\ \end{matrix}\right]= \left[ \begin{matrix} 10&0 \\ 0&10 \\ -2&-3 \\ \end{matrix}\right]
\left[ \begin{matrix} 4&-3&0&0&1 \\ 0&0&0&8&0 \\ 0&0&1&0&0 \\ 0&0&0&0&0 \end{matrix}\right] \times \left[ \begin{matrix} 3&0&0 \\ 4&2&0 \\ 0&1&0 \\ 1&0&0 \\ 0&0&0 \end{matrix}\right] = \left[ \begin{matrix} 0&-6&0 \\ 8&0&0 \\ 0&1&0 \\ 0&0&0 \end{matrix}\right]
二、概要设计
1.数据结构
ADT SparseMatrix{
数据对象:D={ aij | i = 1,2,...,m; j = 1,2,...,n;
aij∈ElemSet, m和n分别为矩阵的行数和列数}
数据关系:R={Row,Col}
Row={|1≤i≤m,1≤j≤n-1 }
Col={|1≤i≤m-1,1≤j≤n }
基本操作:
CreateSMatrix(&M)
操作结果:创建稀疏矩阵M。
PrintSMatrix(M)
初始条件:稀疏矩阵M存在。
操作结果: 输出稀疏矩阵M。
AddSMatrix(M, N, &Q)
初始条件:稀疏矩阵M与N的行数和列数对应相等。
操作结果:求稀疏矩阵的和Q=M+N。
SubtSMatrix(M, N, &Q)
初始条件:稀疏矩阵M与N的行数和列数对应相等。
操作结果:求稀疏矩阵的差Q=M-N。
MultSMatrix(M, N, &Q)
初始条件:稀疏矩阵M的列数等于N的行数。
操作结果:求稀疏矩阵乘积Q=M*N。
}ADT SparseMatrix
2. 使用函数
(1)行逻辑链接的顺序表
int CreateSMatrix(RLSMatrix *M)
操作结果:创建稀疏矩阵
int PrintSMatrix(RLSMatrix M)
操作结果:打印稀疏矩阵
int AddSMatrix(RLSMatrix M, RLSMatrix N, RLSMatrix *Q)
操作结果:稀疏矩阵加法,Q=M+N
int SubSMatrix(RLSMatrix M, RLSMatrix N, RLSMatrix *Q)
操作结果:稀疏矩阵减法,Q=M-N
int MultSMatrix(RLSMatrix M, RLSMatrix N, RLSMatrix *Q)
操作结果:稀疏矩阵乘法,Q=M*N
(2)十字链表
int CreateSMatrix_OL(CrossList *M)
操作结果:创建稀疏矩阵
int PrintSMatrix_OL(CrossList M)
操作结果:打印稀疏矩阵
int AddSMatrix_OL(CrossList *A, CrossList *B)
操作结果:将稀疏矩阵B加到稀疏矩阵A上
int SubSMatrix_OL(CrossList *A, CrossList *B)
操作结果:在稀疏矩阵A上减去稀疏矩阵B
int MultSMatrix_OL(CrossList A, CrossList B, CrossList *C)
操作结果:稀疏矩阵乘法,C=A*B
三、详细设计
1. 数据储存结构
(1)行逻辑链接顺序表
#define MAXSIZE 400 // 最大非零元个数
#define MAXRC 20 // 最大行数和列数
typedef struct{
int i,j; // 该非零元的行下标和列下标
int e;
}Triple;
typedef struct{
Triple data[MAXSIZE+1]; // 非零元三元组表
int rpos[MAXRC+1]; // 各行第一个非零元位置表
int mu,nu,tu; // 矩阵行数、列数和非零元个数
}RLSMatrix;
(2)十字链表
#define MAXSIZE 400 // 最大非零元个数
#define MAXRC 20 // 最大行数和列数
typedef struct OLNode{
int i,j; // 该非零元的行和列下标
int e;
struct OLNode *right, *down; // 该非零元所在行表和列表的后继链域
}OLNode, *OLink;
typedef struct {
OLink *rhead, *chead; // 行和列链表头指针向量基址由CreateSMatrix分配
int mu, nu, tu; // 稀疏矩阵行数、列数和非零元个数
}CrossList;
2.基本功能实现
2.1行逻辑链接顺序表
(1)稀疏矩阵创建
分别读入矩阵行数、列数以及非零元个数,同时提示输入各个非零元位置和数值,对于输入的非零元判断是否合法,并根据行号和列号进行以行为主序的储存,最后计算各行首个非零元位置。
int CreateSMatrix(RLSMatrix *M){
if(M) free(M);
int m,n,t;
do{
printf("输入矩阵行数,列数和非零元数目,用空格隔开\n");
scanf("%d %d %d", &m, &n, &t);
if(m < 0 || n < 0 || t < 0 || t > m*n){
printf("参数错误 \n");
}
}while(m < 0 || n < 0 || t < 0 || t > m*n);
M->mu = m; M->nu = n; M->tu = t;
printf("请按行优先输入三元组\n");
int i,j,e,k;
int p,q;
for(k = 1; k <= t; k++){
do{
printf("输入第%d个非零元的行号 列号 值, 用空格隔开\n",k);
scanf("%d %d %d", &i, &j, &e);
if(i <= 0 || j <= 0 || i > m || j > n|| e==0){
printf("参数错误\n");
}
}while(i <= 0 || j <= 0 || i > m || j > n|| e==0);
for(p = 1; p <= k-1 && (i > M->data[p].i || (i == M->data[p].i && j > M->data[p].j)); p++); //找到三元组插入的位置
for(q = k-1;q >= p; q--) M->data[q+1] = M->data[q]; //行序大的三元组依次向后移动
M->data[p].i = i;
M->data[p].j = j;
M->data[p].e = e;
}
int row,x;
int num[MAXRC + 1];
for(row = 1; row <= M->mu; ++row) num[row] = 0;
for(x = 1; x <= M->tu; ++x) ++num[M->data[x].i]; // 每一行非零元个数
M->rpos[1] = 1;
for(row = 2; row<=M->mu; ++row) M->rpos[row] = M->rpos[row-1] + num[row-1]; // 各行首个非零元位置
return 1;
}
(2)稀疏矩阵加减法
加减法基本思路一致,这里以加法为例。找到M和N矩阵各行非零元素的起始和终末位置之后开始遍历,对列号相等和不等两种情况分别处理,并处理相加结果等于0的情况。
int AddSMatrix(RLSMatrix M, RLSMatrix N, RLSMatrix *Q){
int arow;
int p,q,s = 1;
int tp,tq;
if(M.mu != N.mu || M.nu != N.nu) return 0;
Q->mu = M.mu; Q->nu = M.nu;
for(arow = 1; arow <= M.mu; ++arow){
Q->rpos[arow] = Q->tu + 1;
p = M.rpos[arow]; q = N.rpos[arow];
if(arow < M.mu){
tp = M.rpos[arow + 1];
tq = N.rpos[arow + 1];
}
else{
tp = M.tu + 1;
tq = N.tu + 1;
}
while(p < tp && q < tq){
if(M.data[p].j != N.data[q].j){ // 列号不等
if(M.data[p].j < N.data[q].j){
Q->data[s].i = arow;
Q->data[s].j = M.data[p].j;
Q->data[s].e = M.data[p].e;
s++; p++; Q->tu++;
}else{
Q->data[s].i = arow;
Q->data[s].j = N.data[q].j;
Q->data[s].e = N.data[q].e;
s++; q++; Q->tu++;
}
}else{ // 列号相等
if(M.data[p].e + N.data[q].e != 0){ // 结果非零
Q->data[s].i = arow;
Q->data[s].j = M.data[p].j;
Q->data[s].e = M.data[p].e + N.data[q].e;
s++; q++; p++; Q->tu++;
}else{q++; p++;}
}
}
while(p < tp){
Q->data[s].i = arow;
Q->data[s].j = M.data[p].j;
Q->data[s].e = M.data[p].e;
p++; s++; Q->tu++;
}
while(q < tq){
Q->data[s].i = arow;
Q->data[s].j = N.data[q].j;
Q->data[s].e = N.data[q].e;
q++; s++; Q->tu++;
}
}
return 1;
}
(3)稀疏矩阵乘法
对于M中每个元素M.data[p]找到N中所有满足条件M.data[p].j=N.data[q].i的元素N.data[q],求得M.data[p].e * N.data[q].e,对Q中元素设计累计和变量,初值为零,扫描数组M,求得相应元素乘积累加到适当的求累计和变量上。Q中元素行号与M中元素行号一致,又M中元素以M的行序为主序,因此对Q进行逐行处理,先求得累计和的中间结果(Q的一行),再压缩储存到Q.data中去。
int MultSMatrix(RLSMatrix M, RLSMatrix N, RLSMatrix *Q){
// 求矩阵乘积Q=M×N,采用行逻辑链接储存表示