稀疏矩阵的十字链式压缩存储:
代码演示:
/*
* 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");
}
}