稀疏矩阵之十字链表压缩存储并且实现两个稀疏矩阵相加

稀疏矩阵的十字链式压缩存储:

在这里插入图片描述

代码演示:

/*
 * Date: 2020/11/10
 * Author: XiaoXiangWei
 * Work: Sparse matrix addition
 * */

#include <stdio.h>
#include <stdlib.h>

#define   OK           1
#define   ERROR        0
#define   OVERFLOW    -2

typedef int Status;

// 定义节点结构体
typedef struct scNode {
    int row,col; // 行列坐标
    int data;
    struct scNode *right,*down; // 同行右节点,同列下节点
}Node, *LNode;


// 构建十字链表结构体
typedef struct crossLink {
    LNode *rHead, *cHead; // 行头指针,列头指针
    int rN,cN,tN; // 行数,列数, 非零元素个数
}Cross, *lCross;

// 函数定义: 创建十字链表;十字链表相加;打印结果
int CreateSMatrix(lCross L);
int getAdd(lCross L,lCross S);
int PrintCross(lCross L);

int main(){

    // 分配空间
    lCross L = (lCross)malloc(sizeof(Cross));
    lCross S = (lCross)malloc(sizeof(Cross));

    // 创建十字链表
    printf("请输入第一个稀疏矩阵行列数:");
    CreateSMatrix(L);
    printf("\n请输入第二个稀疏矩阵行列数:");
    CreateSMatrix(S);

    // 相加
    getAdd(L,S);

    // 打印
    printf("============相加============\n");
    PrintCross(L);
}

Status CreateSMatrix(lCross L){
    int r,c,t; // 行数,列数, 非零元素个数
    scanf("%d%d", &r,&c); // 取值
    L->rN = r, L->cN = c ; // 赋值

    /* +1,方便计算 */
    if (!(L->rHead = (LNode *)malloc((L->rN+1)*sizeof(Node)))) return ERROR; // 分配行头指针空间,创建失败则返回 ERROR
    if (!(L->cHead = (LNode *)malloc((L->cN+1)*sizeof(Node)))) return ERROR; // 分配列头指针空间,创建失败则返回 ERROR

    for (int i = 1; i <= r; ++i) L->rHead[i] = NULL;  // 初始化置NULL
    for (int i = 1; i <= c; ++i) L->cHead[i] = NULL;  // 初始化置NULL

    printf("请输入稀疏矩阵:\n");
    int ele; // 定义输入元素
    for (int i = 1; i <= L->rN; ++i) {
        for (int j = 1; j <= L->cN; ++j) {
            scanf("%d",&ele);
            if (ele!=0){
                t++; // 非零元素个数累加
                LNode p,q; // 定义两个临时节点,中间人
                if (!(p = (LNode)malloc(sizeof(Node)))) exit(OVERFLOW); // 创建一个 p指针节点,用于储存输入的数据

                // 节点赋值操作
                p->row = i; p->col = j; p->data = ele;
                p->right = NULL; p->down = NULL;

                // 寻找在 行 表中的插入位置
                if (L->rHead[i] == NULL  || L->rHead[i]->col > j){ 
                    p->right = L->rHead[i]; // 可写成 p-> right = NULL, 但这里的意义又是什么呢,个人认为可以删减,下同
                    L->rHead[i] = p; // 让 L 的行头结点指向 新节点
                } else {
                   q = L->rHead[i];
                   while (q->right && q->right->col < j){ // 递归一直找到 一行中最右边的节点, q->right->col < j 意义 : ???
                       q = q->right;
                   }
                   p->right = q->right; // 此时 q 是一行的最右边的节点,所以 q->right = NULL 必然成立,此处目的是 使得 p->right = NULL;
                   q->right = p; // 接入新节点
                }


                // 寻找在 列 表中的插入位置, 步骤解析上同
                if (L->cHead[j] == NULL || L->cHead[j]->row > i) {
                    p->down = L->cHead[j];
                    L->cHead[j] = p;
                } else {
                    q  = L->cHead[j];
                    while (q->down && q->down->row < i){
                        q = q->down;
                    }
                    p->down = q->down;
                    q->down = p;
                }
            }
        }
    }
    L->tN = t; // 非零个数赋值
    return OK;
}

Status getAdd(lCross L, lCross S){
    LNode temp,p,q;

    for (int j = 1; j <= L->rN; ++j) {  // 以行为基准,一行一行的元素相加, 也可以改成 一列一列相加, 下面 right 同一改成 down
        if (S->rHead[j] == NULL) continue; // 如果 S->rHead[j] == NULL 直接跳出这次循环进入下一次,因为 L->rHead[j]->data + 0 不变
        else {
            if (L->rHead[j] == NULL) { // 如果L->rHead[j] == NULL, 下面操作则是直接把对应相同坐标S->rHead[j]的值赋给 L->rHead[j]

                /*标准步骤*/
                // temp = (LNode)malloc(sizeof(Node));
                // temp->col = S->rHead[j]->col;
                // temp->row = S->rHead[j]->row;
                // temp->data = S->rHead[j]->data;
                // temp->right = NULL;
                //
                // L->rHead[j] = temp;

                /*个人认为可以直接 L->rHead[j] = S->rHead[j] ; L->rHead[j]->right = NULL,不必上述如此繁琐*/
                L->rHead[j] = S->rHead[j];
                L->rHead[j]->right = NULL;

                S->rHead[j] = S->rHead[j]->right; // S 向右移一位,找到下一右节点
            }

            if (S->rHead[j] == NULL) continue; // 再次判断 S->rHead[j] 同行是否还有右节点,NULL 则跳出这次循环,同上 if (S->rHead[j] == NULL) continue 解释相同

            /*双重for循环,进行两矩阵节点值之间后续的相加操作*/
            for ( p=S->rHead[j] ; ; p = p->right ) {
                for ( q=L->rHead[j] ; ; q = q->right ) {
                    if(q->col == p->col){ // 如果 q->col == p->col 说明当前 p != NULL && q!= NULL,所有直接两矩阵相同坐标位置值相加
                        q->data += p->data;
                        break; // 每次都要break退出循环,结束当前行中的操作的这个节点相加操作,进入下一循环进行下一节点相加操作,下同
                    } else if (q == L->rHead[j] && p->col < q->col){ // 为什么要干
                        // 创建临时节点
                        temp=(LNode)malloc(sizeof(Node));

                        // 节点赋值
                        temp->col   = p->col;
                        temp->row   = p->row;
                        temp->data  = p->data;
                        temp->right = q->right;

                        temp->right = q;
                        L->rHead[j] = temp;

                        break;
                    } else if((q->right == NULL || q->right->col > p->col)  && p->col > q->col){ // 这里的判断也不怎么理解

                        // 创建新节点
                        temp=(LNode)malloc(sizeof(Node));

                        // 节点赋值
                        temp->col   = p->col;
                        temp->row   = p->row;
                        temp->data  = p->data;
                        temp->right = q->right;

                        q->right=temp;
                        break;
                    }
                }
                if( p->right == NULL ) break; // 一行节点操作结束跳出循环
            }
        }
    }
}

Status PrintCross(lCross L){
    LNode pTemp;
    for(int p = 1; p <= L->rN; p++){
        pTemp=L->rHead[p];
        for(int q = 1; q <= L->cN; q++){
            if(pTemp != NULL && pTemp->col == q){
                if (pTemp->data < 10) {
                    printf("%d  ",pTemp->data);
                } else {
                    printf("%d ",pTemp->data);
                }
                pTemp = pTemp->right;
            }
            else
                printf("0  ");
        }
        printf("\n");
    }
}

效果展示:

在这里插入图片描述

  • 2
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
下面是十字链表实现两个稀疏矩阵相加的C语言代码: ``` #include <stdio.h> #include <stdlib.h> #define OK 1 #define ERROR 0 #define TRUE 1 #define FALSE 0 #define MAXSIZE 1000 typedef int Status; typedef struct OLNode { int i, j; //非零元素的行列下标 int e; //非零元素的值 struct OLNode *right, *down; //右指针和下指针 } OLNode, *OLink; typedef struct { OLink *rhead, *chead; //行指针和列指针 int m, n, len; //稀疏矩阵的行数、列数和非零元素个数 } CrossList; //十字链表按行插入新节点 Status InsertOLNodeByRow(OLink *rhead, OLink *chead, int i, int j, int e) { OLink p = *rhead, pre = NULL, q = NULL; q = (OLink)malloc(sizeof(OLNode)); q->i = i; q->j = j; q->e = e; while (p && p->i < i) { pre = p; p = p->down; } if (p && p->i == i && p->j < j) { while (p && p->j < j && p->i == i) { pre = p; p = p->right; } } if (p && p->i==i && p->j==j) { p->e += e; free(q); return OK; } q->down = p; if (pre) pre->down = q; else *rhead = q; p = chead[j]; pre = NULL; while (p && p->j<j) { pre = p; p = p->right; } q->right = p; if (pre) pre->right = q; else chead[j] = q; return OK; } //创建稀疏矩阵 Status CreateSMatrix(CrossList *M) { int i, j, k, e; printf("请输入稀疏矩阵的行数、列数和非零元素个数:"); scanf("%d %d %d", &M->m, &M->n, &M->len); M->rhead = (OLink*)malloc((M->m+1)*sizeof(OLink)); M->chead = (OLink*)malloc((M->n+1)*sizeof(OLink)); for (i=0; i<=M->m; i++) M->rhead[i] = NULL; for (i=0; i<=M->n; i++) M->chead[i] = NULL; printf("请依次输入%d个非零元素及其行列下标:\n", M->len); for (k=1; k<=M->len; k++) { printf("第%d个:", k); scanf("%d %d %d",&i,&j,&e); if (InsertOLNodeByRow(&(M->rhead[i]), M->chead, i, j, e) != OK) { printf("插入结点失败!\n"); return ERROR; } } return OK; } //按十字链表格式输出稀疏矩阵 void PrintSMatrix(CrossList M) { int i, j; OLink p = NULL; printf("稀疏矩阵的行数、列数和非零元素个数分别为:%d %d %d\n", M.m, M.n, M.len); for (i=1; i<=M.m; i++) { p = M.rhead[i]; for (j=1; j<=M.n; j++) { if (p && p->j == j) { printf("%d\t", p->e); p = p->right; } else { printf("0\t"); } } printf("\n"); } } //稀疏矩阵相加,结果存储在M3中 Status AddSMatrix(CrossList M1, CrossList M2, CrossList *M3) { if (M1.m != M2.m || M1.n != M2.n) { printf("两个矩阵的行数或列数不等,无法相加!\n"); return ERROR; } int i, j; int e; *M3 = M1; M3->rhead = (OLink*)malloc((M1.m+1)*sizeof(OLink)); M3->chead = (OLink*)malloc((M1.n+1)*sizeof(OLink)); for (i=0; i<=M1.m; i++) M3->rhead[i] = NULL; for (i=0; i<=M1.n; i++) M3->chead[i] = NULL; OLink p = NULL, q = NULL; for (i=1; i<=M1.m; i++) { p = M1.rhead[i]; q = M2.rhead[i]; while (p && q) { if (p->j < q->j) { if (InsertOLNodeByRow(&(M3->rhead[i]), M3->chead, p->i, p->j, p->e) != OK) { printf("插入结点失败!\n"); return ERROR; } p = p->right; } else if (p->j > q->j) { if (InsertOLNodeByRow(&(M3->rhead[i]), M3->chead, q->i, q->j, q->e) != OK) { printf("插入结点失败!\n"); return ERROR; } q = q->right; } else { e = p->e + q->e; if (InsertOLNodeByRow(&(M3->rhead[i]), M3->chead, p->i, p->j, e) != OK) { printf("插入结点失败!\n"); return ERROR; } p = p->right; q = q->right; } } while (p) { if (InsertOLNodeByRow(&(M3->rhead[i]), M3->chead, p->i, p->j, p->e) != OK) { printf("插入结点失败!\n"); return ERROR; } p = p->right; } while (q) { if (InsertOLNodeByRow(&(M3->rhead[i]), M3->chead, q->i, q->j, q->e) != OK) { printf("插入结点失败!\n"); return ERROR; } q = q->right; } } return OK; } int main() { CrossList M1, M2, M3; CreateSMatrix(&M1); CreateSMatrix(&M2); printf("第一个稀疏矩阵:\n"); PrintSMatrix(M1); printf("第二个稀疏矩阵:\n"); PrintSMatrix(M2); AddSMatrix(M1, M2, &M3); printf("相加后的稀疏矩阵:\n"); PrintSMatrix(M3); return 0; } ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

m0rta1

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值