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

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

在这里插入图片描述

代码演示:

/*
 * 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");
    }
}

效果展示:

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/haduwi/article/details/109649559