数据结构:稀疏矩阵运算器

题目:设计一个程序,实现一个能进行稀疏矩阵基本运算的计算器。按照教科书《数据结构(C语言版)》(严蔚敏等)5.3.2节中描述的方法,以十字链表表示稀疏矩阵。

一、需求分析

  1. 稀疏矩阵是指那些多数元素为零的矩阵。利用“稀疏”特点进修学校储存和计算可以大大节省储存空间,提高计算效率。实现一个能进行稀疏矩阵基本运算的计算器。
    以“带行逻辑链接信息”的三元组顺序表表示稀疏矩阵,实现两个矩阵相加、相减和相乘的运算。稀疏矩阵的输入形式采用三元组表示,而运算结果的矩阵则以通常的阵列形式列出。
  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={<ai,j,ai,j+1 >|1≤i≤m,1≤j≤n-1 }
Col={<ai,j,ai+1,j>|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,采用行逻辑链接储存表示
    int arow,brow;
    int p,q,t,tp;
    int ccol;
    int ctemp[MAXRC + 1];
    if(M.nu != N.mu) return 0;
    Q->mu = M.mu; Q->nu = N.nu; Q->tu = 0;  // Q初始化
    if(M.tu * N.tu != 0){   // Q是非零矩阵
        for(arow = 1; arow <= M.mu; ++arow){    // 处理M的每一行
            for(int i = 1; i <= N.nu; i++) ctemp[i] = 0;    // 当前行各元素累加器清零
            Q->rpos[arow] = Q->tu + 1;
            if(arow < M.mu) tp = M.rpos[arow + 1];
            else{tp = M.tu + 1;}
            for(p = M.rpos[arow]; p<tp; ++p){   // 对当前行每一个非零元
                brow = M.data[p].j;     // 找到对应元所在N中的行号
                if(brow < N.mu) t = N.rpos[brow + 1];
                else{t = N.tu + 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;
                }// for q
             }// 求得Q中第crow(=arow)行的非零元
             for(ccol = 1; ccol <= Q->nu; ++ccol){    //压缩储存改行非零元
                 if(ctemp[ccol]){
                    if(++Q->tu > MAXSIZE) return 0;
                    Q->data[Q->tu].i = arow;
                    Q->data[Q->tu].j = ccol;
                    Q->data[Q->tu].e = ctemp[ccol];
                }// if
             }  // for ccol
        }// for arow
    }// if
    return 1;
}// MultSMatrix

(4)稀疏矩阵输出

通过双重循环,打印稀疏矩阵中非零元和零元素

int PrintSMatrix(RLSMatrix M){
    int row,col,t = 1;
    for(row = 1; row <= M.mu; row++){
        for(col = 1; col <= M.nu; col++){
            if(M.data[t].i == row && M.data[t].j == col){
                printf("%d\t",M.data[t].e);
                t++;
            }else{
                printf("0\t");
            }
        }
        printf("\n");
    }
    return 1;
}

2.2十字链表

(1)稀疏矩阵创建

读入矩阵行数、列数和非零元个数,同时提示输入各个非零元位置和数值,对于输入的非零元判断是否合法,生成结点并完成在行链表和列链表中的插入。

int CreateSMatrix_OL(CrossList *M){
    if(M) free(M);
    int c;
    int m,n,t;
    int i,j,k,e;
    OLink p,q;
    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;
    if(!(M->rhead = (OLink*)malloc((m+1)*sizeof(OLink)))) exit(-2);
    if(!(M->chead = (OLink*)malloc((n+1)*sizeof(OLink)))) exit(-2);
    for(c = 1; c <= M->mu; c++) M->rhead[c]=NULL;   // 初始化行列头指针向量
    for(c = 1; c <= M->nu; c++) M->chead[c]=NULL;
    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);
        if(!(p = (OLNode*)malloc(sizeof(OLNode)))) exit(-2);
        p->i = i; p->j = j; p ->e = e;  // 生成结点
        if(M->rhead[i] == NULL || M->rhead[i]->j > j){
            p->right = M->rhead[i];
            M->rhead[i] = p;
        }else{  // 寻找行表中的插入位置
            for(q = M->rhead[i]; (q->right) && (q->right->j < j); q = q->right);
            p->right = q->right;
            q->right = p;
        }   // 完成行插入  
        if(M->chead[j] == NULL || M->chead[j]->i > i){  // 寻找列表中的插入位置
            p->down = M->chead[j];
            M->chead[j] = p;
        }else{
            for(q = M->chead[j]; (q->down) && (q->down->i < i); q = q->down);
            p->down = q->down;
            q->down = p;
        }   // 完成列插入
    }
    return 1;
}

(2)稀疏矩阵加减法

加减法基本思路一致,这里以加法为例。
非空指针pa和pb分别指向矩阵A,B中行值相同的两个结点,pa==NULL表明矩阵A在改行没有非零元,分为以下四种情况:

  • 若pa==NULL或pa->j>pb->i,则需要在A矩阵的链表中插入一个值为bij的结点,此时,需要改变同一行中前一结点的right域值,以及同一列中前一结点的down域值。
  • 若pa->j<pb->j,则将pa指针向右推进一步
  • 若pa->j==pb->j,且pa->e+pb->e!=0,则只要将aij+bij的值送到pa所指结点的e域即可,其他所有域的值都不变
  • 若pa->j==pb->j,且pa->e+pb->e==0,则需要在A矩阵链表中删除pa所指结点,此时需要改变同一行中前一结点的right域值以及同一列前一结点的down阈值。
    为了便于插入和删除节点,还需要设置一些辅助指针。其一是在A的行链表上设pre指针,指示pa所指结点的前驱结点;其二是在A的每一列的链表上设一个指针hl[j],其初值和列链表的头指针相同,即hl[j]=chead[j]
int AddSMatrix_OL(CrossList *A, CrossList *B){
    OLink pa, pb, pre, p, hl[MAXRC + 1];
    int i, j;
    if(A->mu != B->mu || A->nu != B->nu) return 0;
    for(j = 1; j <= A->nu; j++) hl[j] = A->chead[j];
    for(i = 1; i <= A->mu; i++){
        pa = A->rhead[i]; pb = B->rhead[i]; pre = NULL;
        while(pb){
            if(!(p = (OLNode*)malloc(sizeof(OLNode)))) exit(-2);
            p->i = pb->i; p->j = pb->j; p->e = pb->e;
            if(pa == NULL || pa->j > pb->j){ // 在A中插入pb所指结点的复制结点p 
                if(pre == NULL) A->rhead[p->i] = p;
                else{pre->right = p; }
                p->right = pa;
                pre = p;
                if(A->chead[p->j] == NULL || A->chead[p->j]->i > p->i){
                    p->down = A->chead[p->j];
                    A->chead[p->j] = p;
                }else{  // 在hl[p->j]中找到新结点的前驱,插入结点
                    p->down = hl[p->j]->down;
                    hl[p->j]->down = p;
                }
                hl[p->j] = p;
                pb = pb->right;
                A->tu++;
            }else if(pa && pa->j < pb->j){  // pa指向本行下一个非零结点
                pre = pa;
                pa = pa->right;
            }else if(pa->j == pb->j){   // 加和
                pa->e += pb->e;
                if(pa->e == 0){ // 加和为0,删除结点,修改行表和列表相应指针
                    if(pre == NULL) A->rhead[pa->i] = pa->right;
                    else{pre->right = pa->right; }
                    p = pa; pa = pa->right;
                    if(A->chead[p->j] == p) A->chead[p->j] = hl[p->j] = p->down;
                    else{hl[p->j]->down = p->down; }
                    free(p);
                    pb = pb->right;
                    A->tu--;
                }else{pre = pa; pa = pa->right; pb = pb->right; }
            }
        }
    }
    return 1;
}

(3)稀疏矩阵乘法

对于C中i行j列的元素,分别使p指向A中i行链表头指针,q指向B中j列链表头指针,累加和e置为零。p,q不为空时,

  • 当p->j > q->i,q指针下移
  • 当p->j < q->i,p指针右移
  • 当p->j == q->i,对乘积累计求和,并移动指针
    累加器e不为0时,则在矩阵C行列链表中插入节点p1,设置cpre和rpre分别指示行列链表中p1的前驱结点。
int MultSMatrix_OL(CrossList A, CrossList B, CrossList *C){
    int i, j, e;
    OLink p, q, p1, rpre, cpre;
    if(A.nu != B.mu) return 0;
    C->mu = A.mu; C->nu =  B.nu; C->tu = 0;     // 初始化矩阵C
    if(!(C->rhead = (OLink*)malloc((C->mu+1)*sizeof(OLink)))) exit(-2);
    if(!(C->chead = (OLink*)malloc((C->nu+1)*sizeof(OLink)))) exit(-2);
    for(i = 1; i <= C->mu; i++) C->rhead[i] = NULL;
    for(j = 1; j <= C->nu; j++) C->chead[j] = NULL;
    for(i = 1; i <= C->mu; i++){
        for(j = 1; j <= C->nu; j++){
            p = A.rhead[i]; q = B.chead[j]; e = 0;  // p,q分别指向A中该行链表头指针,B中该列链表头指针
            while(p && q){  
                if(p->j > q->i){    
                    q = q->down;
                }else if(p->j < q->i){
                    p = p->right;
                }else{  
                    e += p->e * q->e; // 乘积累加
                    q = q->down;
                    p = p->right;
                }
            }
            if(e){  // 累加不为0
                C->tu++;
                if(!(p1 = (OLNode*)malloc(sizeof(OLNode)))) exit(-2);
                p1->i = i; p1->j = j; p1->e = e;
                p1->right = NULL; p1->down = NULL;
                // 行插入
                if(C->rhead[i] == NULL) {C->rhead[i] = p1; rpre = p1;}
                else{rpre->right = p1; rpre = p1; }
                // 列插入
                if(C->chead[j] == NULL) {C->chead[j] = p1; cpre = p1;}
                else{cpre->down = p1; cpre = p1; }

            }
        }
    }
    return 1;
}

(4)稀疏矩阵输出

通过双重循环,打印稀疏矩阵中非零元和零元素

int PrintSMatrix_OL(CrossList M){
    OLink p;
    int row,col;
    for(row = 1; row <= M.mu; row++){
        if(M.rhead[row]){
            p = M.rhead[row];
            for(col = 1; col <= M.nu; col++){
                if(p->i == row && p->j == col && p != NULL){
                    printf("%d\t",p->e);
                    if(p->right) p = p->right;
                }else{printf("0\t");}
            }
        }else{for(col = 1; col <= M.nu; col++) printf("0\t");}
        printf("\n");
    }
    return 1;
}

4.主程序

通过读入操作序号来进行相应操作,在满足操作对应条件下进行相应操作,并打印结果,两种储存方式下主程序结构基本一致,只是运算时调用函数不同。

int main(){
    int num;
    CrossList M, N, Q;
    do{
        printf("操作:1.矩阵相加 2.矩阵相减 3.矩阵相乘 4.退出\n");
        scanf("%d",&num);
        if(num == 1 || num == 2 || num == 3){
            printf("请输入矩阵1\n");
            CreateSMatrix_OL(&M);
            printf("请输入矩阵2\n");
            CreateSMatrix_OL(&N);
            printf("两矩阵为:\n");
            PrintSMatrix_OL(M);
            printf("\n");
            PrintSMatrix_OL(N);
            switch(num){
            case 1:
                {
                    if(AddSMatrix_OL(&M, &N)){
                        printf("结果:\n");
                        PrintSMatrix_OL(M);
                    }
                    else {printf("参数错误\n");}
                    break;
                }
            case 2:
                {

                    if(SubSMatrix_OL(&M, &N)){
                        printf("结果:\n");
                        PrintSMatrix_OL(M);
                    }
                    else {printf("参数错误\n");}
                    break;
                }
            case 3:
                {
                    if(MultSMatrix_OL(M,N,&Q)){
                        printf("结果:\n");
                        PrintSMatrix_OL(Q);
                    }
                    else {printf("参数错误\n");}
                    break;
                }
            default:  break;
            }
        }
    }while(num == 1 || num == 2 || num == 3);
    return 1;
}

5.程序的层次结构

层次结构

四、用户手册

  1. 本程序的运行环境为DOS操作系统,执行文件为:sparsematrix.exe和crossmatrix.exe 分别对应行逻辑链接顺序表和十字链表实现的稀疏矩阵计算器
  2. 进入程序按提示操作,输入要进行操作对应的序号
  3. 根据操作提示输入两矩阵的行数、列数和非零元个数,并输入非零元的行号,列号以及值
  4. 结果打印
  5. 根据操作提示退出程序

五、测试结果

\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
测试结果2
测试结果3

六、源代码

sparsematrix.c

#include<stdio.h>
#include<stdlib.h>
#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;

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;
}


int PrintSMatrix(RLSMatrix M){
    int row,col,t = 1;
    for(row = 1; row <= M.mu; row++){
        for(col = 1; col <= M.nu; col++){
            if(M.data[t].i == row && M.data[t].j == col){
                printf("%d\t",M.data[t].e);
                t++;
            }else{
                printf("0\t");
            }
        }
        printf("\n");
    }
    return 1;
}


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;
}

int SubSMatrix(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;
}


int MultSMatrix(RLSMatrix M, RLSMatrix N, RLSMatrix *Q){
    // 求矩阵乘积Q=M×N,采用行逻辑链接储存表示
    int arow,brow;
    int p,q,t,tp;
    int ccol;
    int ctemp[MAXRC + 1];
    if(M.nu != N.mu) return 0;
    Q->mu = M.mu; Q->nu = N.nu; Q->tu = 0;  // Q初始化
    if(M.tu * N.tu != 0){   // Q是非零矩阵
        for(arow = 1; arow <= M.mu; ++arow){    // 处理M的每一行
            for(int i = 1; i <= N.nu; i++) ctemp[i] = 0;    // 当前行各元素累加器清零
            Q->rpos[arow] = Q->tu + 1;
            if(arow < M.mu) tp = M.rpos[arow + 1];
            else{tp = M.tu + 1;}
            for(p = M.rpos[arow]; p<tp; ++p){   // 对当前行每一个非零元
                brow = M.data[p].j;     // 找到对应元所在N中的行号
                if(brow < N.mu) t = N.rpos[brow + 1];
                else{t = N.tu + 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;
                }// for q
             }// 求得Q中第crow(=arow)行的非零元
             for(ccol = 1; ccol <= Q->nu; ++ccol){    //压缩储存改行非零元
                 if(ctemp[ccol]){
                    if(++Q->tu > MAXSIZE) return 0;
                    Q->data[Q->tu].i = arow;
                    Q->data[Q->tu].j = ccol;
                    Q->data[Q->tu].e = ctemp[ccol];
                }// if
             }  // for ccol
        }// for arow
    }// if
    return 1;
}// MultSMatrix


int main(){
    int num;
    RLSMatrix M, N, Q;
    do{
        printf("操作:1.矩阵相加 2.矩阵相减 3.矩阵相乘 4.退出\n");
        scanf("%d",&num);
        if(num == 1 || num == 2 || num == 3){
            printf("请输入矩阵1\n");
            CreateSMatrix(&M);
            printf("请输入矩阵2\n");
            CreateSMatrix(&N);
            printf("两矩阵为:\n");
            PrintSMatrix(M);
            printf("\n");
            PrintSMatrix(N);
            switch(num){
            case 1:
                {
                    if(AddSMatrix(M,N,&Q)){
                        printf("结果:\n");
                        PrintSMatrix(Q);
                    }
                    else {printf("参数错误\n");}
                    break;
                }
            case 2:
                {

                    if(SubSMatrix(M,N,&Q)){
                        printf("结果:\n");
                        PrintSMatrix(Q);
                    }
                    else {printf("参数错误\n");}
                    break;
                }
            case 3:
                {
                    if(MultSMatrix(M,N,&Q)){
                        printf("结果:\n");
                        PrintSMatrix(Q);
                    }
                    else {printf("参数错误\n");}
                    break;
                }
            default:  break;
            }
        }
    }while(num == 1 || num == 2 || num == 3);
    return 1;
}

crosmatrix.c

#include<stdio.h>
#include<stdlib.h>
#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;
    int mu, nu, tu;
}CrossList;

int CreateSMatrix_OL(CrossList *M){
    if(M) free(M);
    int c;
    int m,n,t;
    int i,j,k,e;
    OLink p,q;
    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;
    if(!(M->rhead = (OLink*)malloc((m+1)*sizeof(OLink)))) exit(-2);
    if(!(M->chead = (OLink*)malloc((n+1)*sizeof(OLink)))) exit(-2);
    for(c = 1; c <= M->mu; c++) M->rhead[c]=NULL;
    for(c = 1; c <= M->nu; c++) M->chead[c]=NULL;
    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);
        if(!(p = (OLNode*)malloc(sizeof(OLNode)))) exit(-2);
        p->i = i; p->j = j; p ->e = e;
        if(M->rhead[i] == NULL || M->rhead[i]->j > j){
            p->right = M->rhead[i];
            M->rhead[i] = p;
        }else{
            for(q = M->rhead[i]; (q->right) && (q->right->j < j); q = q->right);
            p->right = q->right;
            q->right = p;
        }
        if(M->chead[j] == NULL || M->chead[j]->i > i){
            p->down = M->chead[j];
            M->chead[j] = p;
        }else{
            for(q = M->chead[j]; (q->down) && (q->down->i < i); q = q->down);
            p->down = q->down;
            q->down = p;
        }
    }
    return 1;
}

int PrintSMatrix_OL(CrossList M){
    OLink p;
    int row,col;
    for(row = 1; row <= M.mu; row++){
        if(M.rhead[row]){
            p = M.rhead[row];
            for(col = 1; col <= M.nu; col++){
                if(p->i == row && p->j == col && p != NULL){
                    printf("%d\t",p->e);
                    if(p->right) p = p->right;
                }else{printf("0\t");}
            }
        }else{for(col = 1; col <= M.nu; col++) printf("0\t");}
        printf("\n");
    }
    return 1;
}

int AddSMatrix_OL(CrossList *A, CrossList *B){
    OLink pa, pb, pre, p, hl[MAXRC + 1];
    int i, j;
    if(A->mu != B->mu || A->nu != B->nu) return 0;
    for(j = 1; j <= A->nu; j++) hl[j] = A->chead[j];
    for(i = 1; i <= A->mu; i++){
        pa = A->rhead[i]; pb = B->rhead[i]; pre = NULL;
        while(pb){
            if(!(p = (OLNode*)malloc(sizeof(OLNode)))) exit(-2);
            p->i = pb->i; p->j = pb->j; p->e = pb->e;
            if(pa == NULL || pa->j > pb->j){
                if(pre == NULL) A->rhead[p->i] = p;
                else{pre->right = p; }
                p->right = pa;
                pre = p;
                if(A->chead[p->j] == NULL || A->chead[p->j]->i > p->i){
                    p->down = A->chead[p->j];
                    A->chead[p->j] = p;
                }else{
                    p->down = hl[p->j]->down;
                    hl[p->j]->down = p;
                }
                hl[p->j] = p;
                pb = pb->right;
                A->tu++;
            }else if(pa && pa->j < pb->j){
                pre = pa;
                pa = pa->right;
            }else if(pa->j == pb->j){
                pa->e += pb->e;
                if(pa->e == 0){
                    if(pre == NULL) A->rhead[pa->i] = pa->right;
                    else{pre->right = pa->right; }
                    p = pa; pa = pa->right;
                    if(A->chead[p->j] == p) A->chead[p->j] = hl[p->j] = p->down;
                    else{hl[p->j]->down = p->down; }
                    free(p);
                    pb = pb->right;
                    A->tu--;
                }else{pre = pa; pa = pa->right; pb = pb->right; }
            }
        }
    }
    return 1;
}

int SubSMatrix_OL(CrossList *A, CrossList *B){
    OLink pa, pb, pre, p, hl[MAXRC + 1];
    int i, j;
    if(A->mu != B->mu || A->nu != B->nu) return 0;
    for(j = 1; j <= A->nu; j++) hl[j] = A->chead[j];
    for(i = 1; i <= A->mu; i++){
        pa = A->rhead[i]; pb = B->rhead[i]; pre = NULL;
        while(pb){
            if(!(p = (OLNode*)malloc(sizeof(OLNode)))) exit(-2);
            p->i = pb->i; p->j = pb->j; p->e = - pb->e;
            if(pa == NULL || pa->j > pb->j){
                if(pre == NULL) A->rhead[p->i] = p;
                else{pre->right = p; }
                p->right = pa;
                pre = p;
                if(A->chead[p->j] == NULL || A->chead[p->j]->i > p->i){
                    p->down = A->chead[p->j];
                    A->chead[p->j] = p;
                }else{
                    p->down = hl[p->j]->down;
                    hl[p->j]->down = p;
                }
                hl[p->j] = p;
                pb = pb->right;
                A->tu++;
            }else if(pa && pa->j < pb->j){
                pre = pa;
                pa = pa->right;
            }else if(pa->j == pb->j){
                pa->e -= pb->e;
                if(pa->e == 0){
                    if(pre == NULL) A->rhead[pa->i] = pa->right;
                    else{pre->right = pa->right; }
                    p = pa; pa = pa->right;
                    if(A->chead[p->j] == p) A->chead[p->j] = hl[p->j] = p->down;
                    else{hl[p->j]->down = p->down; }
                    free(p);
                    pb = pb->right;
                    A->tu--;
                }else{pre = pa; pa = pa->right; pb = pb->right; }
            }
        }
    }
    return 1;
}


int MultSMatrix_OL(CrossList A, CrossList B, CrossList *C){
    int i, j, e;
    OLink p, q, p1, rpre, cpre;
    if(A.nu != B.mu) return 0;
    C->mu = A.mu; C->nu =  B.nu; C->tu = 0;
    if(!(C->rhead = (OLink*)malloc((C->mu+1)*sizeof(OLink)))) exit(-2);
    if(!(C->chead = (OLink*)malloc((C->nu+1)*sizeof(OLink)))) exit(-2);
    for(i = 1; i <= C->mu; i++) C->rhead[i] = NULL;
    for(j = 1; j <= C->nu; j++) C->chead[j] = NULL;
    for(i = 1; i <= C->mu; i++){
        for(j = 1; j <= C->nu; j++){
            p = A.rhead[i]; q = B.chead[j]; e = 0;
            while(p && q){
                if(p->j > q->i){
                    q = q->down;
                }else if(p->j < q->i){
                    p = p->right;
                }else{
                    e += p->e * q->e;
                    q = q->down;
                    p = p->right;
                }
            }
            if(e){
                C->tu++;
                if(!(p1 = (OLNode*)malloc(sizeof(OLNode)))) exit(-2);
                p1->i = i; p1->j = j; p1->e = e;
                p1->right = NULL; p1->down = NULL;
                // 行插入
                if(C->rhead[i] == NULL) {C->rhead[i] = p1; rpre = p1;}
                else{rpre->right = p1; rpre = p1; }
                // 列插入
                if(C->chead[j] == NULL) {C->chead[j] = p1; cpre = p1;}
                else{cpre->down = p1; cpre = p1; }

            }
        }
    }
    return 1;
}






int main(){
    int num;
    CrossList M, N, Q;
    do{
        printf("操作:1.矩阵相加 2.矩阵相减 3.矩阵相乘 4.退出\n");
        scanf("%d",&num);
        if(num == 1 || num == 2 || num == 3){
            printf("请输入矩阵1\n");
            CreateSMatrix_OL(&M);
            printf("请输入矩阵2\n");
            CreateSMatrix_OL(&N);
            printf("两矩阵为:\n");
            PrintSMatrix_OL(M);
            printf("\n");
            PrintSMatrix_OL(N);
            switch(num){
            case 1:
                {
                    if(AddSMatrix_OL(&M, &N)){
                        printf("结果:\n");
                        PrintSMatrix_OL(M);
                    }
                    else {printf("参数错误\n");}
                    break;
                }
            case 2:
                {

                    if(SubSMatrix_OL(&M, &N)){
                        printf("结果:\n");
                        PrintSMatrix_OL(M);
                    }
                    else {printf("参数错误\n");}
                    break;
                }
            case 3:
                {
                    if(MultSMatrix_OL(M,N,&Q)){
                        printf("结果:\n");
                        PrintSMatrix_OL(Q);
                    }
                    else {printf("参数错误\n");}
                    break;
                }
            default:  break;
            }
        }
    }while(num == 1 || num == 2 || num == 3);
    return 1;
}
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,839评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,543评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,116评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,371评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,384评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,111评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,416评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,053评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,558评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,007评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,117评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,756评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,324评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,315评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,539评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,578评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,877评论 2 345

推荐阅读更多精彩内容