根据矩阵相加的运算法则,若将矩阵 B加到矩阵A上,对于A的十字链表来说,可能进行的操作有:
① 当 aij 与 bij 均不等于 0,且 aij+bij≠0 时,改变结点的 value 值;
② 当 aij≠0 且 bij=0 时,value 值不变;
③ 当 aij=0且 bij≠0 时,插入一个新结点;
④ 当 aij与 bij 均不等于 0,且 aij+bij=0时,删除一个结点。
进行运算时,从矩阵的第一行开始逐行进行比较,对每一行都从行表头(即A和B在该行中的第一个非零元素结点)出发,假设pa和 pb 分别指向A和 B的十字链表中行值相同的两个结点,该算法基本思想如下:
(1)令pa和 pb 分别指向 A和 B的本行的第一个非零元素的结点。
(2)对本行所有非零结点按以下4种情况进行处理:
① 若 pa->col
②) 若 pa->col>pb->col 或 pa=NULL,则在 A 的十字链表中插入一个新结点,且值为pb 所指结点的 value 值,同时 A的列链表中的指针也要作相应改变;
③ 若 pa->col=pb->col且 pa->v+pb->v≠0,则将 B中当前结点的值与 A 中当前结点的值相加后送给 pa所指结点的 value 值;
④ 若 pa->col=pb->col且 pa->v+pb->v=0,则在 A的十字链表中删除 pa 所指的结点同时 A的列链表中的指针也要作相应改变。
重复以上步骤,直到 B的本行中无非零元素结点结束。
(3)令 pa和 pb 指向下一行的第一个非零元素结点,重复步骤(2)。
另外,为了便于插入和删除结点,需要设置两个指针qa和q,分别指向该结点在同一行中的前驱结点和在同一列中的前驱结点。
# include
# include
# define MAX 100
// 定义稀疏矩阵的节点结构
typedef struct OLNode {
int row, col; // 行号和列号
int v; // 值
struct OLNode *right, *down; // 指向右边和下边的节点指针
} OLNode, *OLink;
// 定义稀疏矩阵的十字链表结构
typedef struct {
OLNode *rhead[MAX], *chead[MAX]; // 行和列的头指针数组
int m, n, t; // 矩阵的行数、列数和非零元素个数
} CrossList;
// 创建稀疏矩阵的十字链表表示
void CreateCrossList(CrossList *M) {
int m, n, t, i, r, c, v;
OLNode *p, *q;
printf("输入矩阵行数、列数和非零元素个数:");
scanf("%d%d%d", &m, &n, &t);
M->m = m;
M->n = n;
M->t = t;
// 初始化行和列的头指针数组
for (i = 1; i <= m; i++)
M->rhead[i] = NULL;
for (i = 1; i <= n; i++)
M->chead[i] = NULL;
// 输入非零元素并插入到十字链表中
for (i = 1; i <= t; i++) {
printf("输入第%d个非零元素的行号、列号和值:", i);
scanf("%d%d%d", &r, &c, &v);
p = (OLNode *)malloc(sizeof(OLNode));
p->row = r;
p->col = c;
p->v = v;
p->right = NULL;
p->down = NULL;
// 插入到行链表中
if (M->rhead[r] == NULL) {
M->rhead[r] = p;
} else {
for (q = M->rhead[r]; q->right && q->right->col < c; q = q->right);
p->right = q->right;
q->right = p;
}
// 插入到列链表中
if (M->chead[c] == NULL) {
M->chead[c] = p;
} else {
for (q = M->chead[c]; q->down && q->down->row < r; q = q->down);
p->down = q->down;
q->down = p;
}
}
}
// 显示稀疏矩阵
void ShowMatrix(CrossList *M) {
int i, j;
OLNode *p;
for (i = 1; i <= M->m; i++) {
p = M->rhead[i];
if (M->rhead[i]) {
for (j = 1; j <= M->n; j++) {
if (p && p->col == j) {
printf("%d ", p->v);
p = p->right;
} else {
printf("0 ");
}
}
} else {
for (j = 1; j <= M->n; j++) {
printf("0 ");
}
}
printf("\n");
}
}
// 查找列链表中指定位置的前驱节点
OLNode *colpred(int r, int c, CrossList *M) {
OLNode *s;
s = M->chead[c];
while (s->down != NULL && s->down->row < r)
s = s->down;
return s;
}
// 矩阵相加
void AddMatrix(CrossList *M1, CrossList *M2, CrossList *M) {
OLNode *p, *q, *ra, *rb, *pa, *pb, *qa;
int i, j;
if (M1->m != M2->m || M1->n != M2->n) {
printf("两个矩阵不能相加!\n");
return;
}
// 初始化矩阵M的行和列头指针数组
for (j = 1; j <= M1->n; j++)
M->chead[j] = M1->chead[j];
M->m = M1->m;
M->n = M1->n;
for (i = 1; i <= M1->m; i++) {
ra = M1->rhead[i];
rb = M2->rhead[i];
pa = ra;
pb = rb;
qa = NULL;
if (M2->rhead[i] == NULL) {
M->rhead[i] = M1->rhead[i];
} else {
while (pb) {
if (pa && pa->col < pb->col) {
if (qa == NULL)
M->rhead[i] = pa;
qa = pa;
pa = pa->right;
} else if (!pa || pa->col > pb->col) {
p = (OLNode *)malloc(sizeof(OLNode));
*p = *pb;
if (qa == NULL)
M->rhead[i] = p;
else
qa->right = p;
p->right = pa;
qa = p;
if (M->chead[pb->col] == NULL) {
M->chead[pb->col] = p;
p->down = NULL;
} else {
q = colpred(p->row, p->col, M);
p->down = q->down;
q->down = p;
}
pb = pb->right;
} else {
pa->v += pb->v;
if (pa->v == 0) {
if (qa == NULL)
M->rhead[i] = pa->right;
else
qa->right = pa->right;
if (M->chead[pb->col] == pa)
M->chead[pb->col] = pa->down;
else {
q = colpred(pa->row, pa->col, M);
q->down = pa->down;
}
free(pa);
} else {
if (qa == NULL)
M->rhead[i] = pa;
qa = pa;
}
pa = pa->right;
pb = pb->right;
}
}
}
}
}
int main() {
CrossList A, B, C;
CreateCrossList(&A); // 创建第一个稀疏矩阵
printf("第一个矩阵为:\n");
ShowMatrix(&A); // 显示第一个稀疏矩阵
CreateCrossList(&B); // 创建第二个稀疏矩阵
printf("第二个矩阵为:\n");
ShowMatrix(&B); // 显示第二个稀疏矩阵
AddMatrix(&A, &B, &C); // 矩阵相加
printf("相加的结果为:\n");
ShowMatrix(&C); // 显示相加后的矩阵
return 0;
}
在数据结构中,稀疏矩阵是一种特殊的矩阵,其中大部分元素为零。对于稀疏矩阵,传统的二维数组存储方式会浪费大量空间。因此,我们通常使用十字链表来存储稀疏矩阵,从而节省存储空间。本文将介绍如何使用C语言实现稀疏矩阵的十字链表表示、显示和矩阵相加操作。
十字链表是一种高效的数据结构,用于存储稀疏矩阵。每个非零元素用一个节点表示,节点包含行号、列号、值,以及指向右边和下边节点的指针。我们定义两个结构体:OLNode
用于存储单个非零元素的信息,CrossList
用于存储整个稀疏矩阵。
typedef struct OLNode {
int row, col; // 行号和列号
int v; // 值
struct OLNode *right, *down; // 指向右边和下边的节点指针
} OLNode, *OLink;
typedef struct {
OLNode *rhead[MAX], *chead[MAX]; // 行和列的头指针数组
int m, n, t; // 矩阵的行数、列数和非零元素个数
} CrossList;
我们通过用户输入来初始化稀疏矩阵。用户依次输入矩阵的行数、列数和非零元素个数,然后输入每个非零元素的行号、列号和值。程序会将这些非零元素插入到十字链表中。
void CreateCrossList(CrossList *M) {
int m, n, t, i, r, c, v;
OLNode *p, *q;
printf("输入矩阵行数、列数和非零元素个数:");
scanf("%d%d%d", &m, &n, &t);
M->m = m;
M->n = n;
M->t = t;
// 初始化行和列的头指针数组
for (i = 1; i <= m; i++)
M->rhead[i] = NULL;
for (i = 1; i <= n; i++)
M->chead[i] = NULL;
// 输入非零元素并插入到十字链表中
for (i = 1; i <= t; i++) {
printf("输入第%d个非零元素的行号、列号和值:", i);
scanf("%d%d%d", &r, &c, &v);
p = (OLNode *)malloc(sizeof(OLNode));
p->row = r;
p->col = c;
p->v = v;
p->right = NULL;
p->down = NULL;
// 插入到行链表中
if (M->rhead[r] == NULL) {
M->rhead[r] = p;
} else {
for (q = M->rhead[r]; q->right && q->right->col < c; q = q->right);
p->right = q->right;
q->right = p;
}
// 插入到列链表中
if (M->chead[c] == NULL) {
M->chead[c] = p;
} else {
for (q = M->chead[c]; q->down && q->down->row < r; q = q->down);
p->down = q->down;
q->down = p;
}
}
}
假设用户输入以下非零元素:
输入矩阵行数、列数和非零元素个数:3 3 4
输入第1个非零元素的行号、列号和值:1 2 5
输入第2个非零元素的行号、列号和值:2 1 3
输入第3个非零元素的行号、列号和值:3 3 7
输入第4个非零元素的行号、列号和值:2 3 2
为了方便查看稀疏矩阵的内容,我们编写了一个函数来显示矩阵。该函数按照行列顺序遍历矩阵,输出非零元素及其位置,其余位置输出0。
void ShowMatrix(CrossList *M) {
int i, j;
OLNode *p;
for (i = 1; i <= M->m; i++) {
p = M->rhead[i];
if (M->rhead[i]) {
for (j = 1; j <= M->n; j++) {
if (p && p->col == j) {
printf("%d ", p->v);
p = p->right;
} else {
printf("0 ");
}
}
} else {
for (j = 1; j <= M->n; j++) {
printf("0 ");
}
}
printf("\n");
}
}
第一个矩阵:
0 5 0
3 0 2
0 0 7
稀疏矩阵的相加操作是将两个矩阵的对应元素相加。我们通过遍历两个矩阵的非零元素,进行相加操作,并将结果存储到一个新的十字链表中。
void AddMatrix(CrossList *M1, CrossList *M2, CrossList *M) {
OLNode *p, *q, *ra, *rb, *pa, *pb, *qa;
int i, j;
if (M1->m != M2->m || M1->n != M2->n) {
printf("两个矩阵不能相加!\n");
return;
}
// 初始化结果矩阵的行和列头指针数组
for (j = 1; j <= M1->n; j++)
M->chead[j] = M1->chead[j];
M->m = M1->m;
M->n = M1->n;
for (i = 1; i <= M1->m; i++) {
ra = M1->rhead[i];
rb = M2->rhead[i];
pa = ra;
pb = rb;
qa = NULL;
if (M2->rhead[i] == NULL) {
M->rhead[i] = M1->rhead[i];
} else {
while (pb) {
if (pa && pa->col < pb->col) {
if (qa == NULL)
M->rhead[i] = pa;
qa = pa;
pa = pa->right;
} else if (!pa || pa->col > pb->col) {
p = (OLNode *)malloc(sizeof(OLNode));
*p = *pb;
if (qa == NULL)
M->rhead[i] = p;
else
qa->right = p;
p->right = pa;
qa = p;
if (M->chead[pb->col] == NULL) {
M->chead[pb->col] = p;
p->down = NULL;
} else {
q = colpred(p->row, p->col, M);
p->down = q->down;
q->down = p;
}
pb = pb->right;
} else {
pa->v += pb->v;
if (pa->v == 0) {
if (qa == NULL)
M->rhead[i] = pa->right;
else
qa->right = pa->right;
if (M->chead[pb->col] == pa)
M->chead[pb->col] = pa->down;
else {
q = colpred(pa->row, pa->col, M);
q->down = pa->down;
}
free(pa);
} else {
if (qa == NULL)
M->rhead[i] = pa;
qa = pa;
}
pa = pa->right;
pb = pb->right;
}
}
}
}
}
第二个矩阵:
复制
0 0 0
0 0 2
0 0 7
相加的结果:
复制
0 5 0
3 0 4
0 0 14
相加逻辑:
如果两个矩阵的行和列不匹配,则无法进行相加操作。
对于每个非零元素,根据其位置(行号和列号)进行相加。
如果某一行或列中只有一个矩阵有非零元素,则直接将该非零元素复制到结果矩阵中。
如果两个矩阵在相同位置都有非零元素,则将它们的值相加,并将结果存储到结果矩阵中。
十字链表的插入操作:
在行链表和列链表中插入节点时,需要维护链表的有序性。
使用前驱节点(qa
)来帮助插入操作,确保插入的节点能够正确连接到链表中。
内存管理:
在节点值相加为零时,需要释放该节点的内存,避免内存泄漏。
用户输入矩阵的行数、列数和非零元素个数,然后依次输入每个非零元素的行号、列号和值。程序将这些非零元素插入到十字链表中。
按照行列顺序遍历矩阵,输出非零元素及其位置,其余位置输出0。
检查两个矩阵的行数和列数是否相同。
遍历两个矩阵的非零元素,进行相加操作。
将结果存储到一个新的十字链表中。
复制
输入矩阵行数、列数和非零元素个数:3 3 4
输入第1个非零元素的行号、列号和值:1 2 5
输入第2个非零元素的行号、列号和值:2 1 3
输入第3个非零元素的行号、列号和值:3 3 7
输入第4个非零元素的行号、列号和值:2 3 2
输入矩阵行数、列数和非零元素个数:3 3 2
输入第1个非零元素的行号、列号和值:2 3 2
输入第2个非零元素的行号、列号和值:3 3 7
第一个矩阵:
复制
0 5 0
3 0 2
0 0 7
第二个矩阵:
复制
0 0 0
0 0 2
0 0 7
相加的结果:
0 5 0
3 0 4
0 0 14
通过本文,我们学习了如何使用C语言实现稀疏矩阵的十字链表表示、显示和矩阵相加操作。十字链表是一种高效的稀疏矩阵存储方式,能够节省大量空间。矩阵相加操作通过遍历两个矩阵的非零元素实现,逻辑清晰且高效。希望本文对大家有所帮助!