数据结构与算法——第5章-数组和广义表-十字链表解决矩阵相加问题(5.10.4)

一 概述

1
2
1.矩阵相加问题
2.实现代码

二 矩阵相加问题

2.1 三种情况

1
2
3
4
5
6
在解决 “将矩阵 B 加到矩阵 A ” 的问题时,由于采用的是十字链表法存储矩阵的三元组,所以在相加的过程中,针对矩阵 B 中每一个非 0 元素,
需要判断在矩阵 A 中相对应的位置,有三种情况:
1. 提取到的 B 中的三元组在 A 相应位置上没有非 0 元素,此时直接加到矩阵 A 该行链表的对应位置上;
2. 提取到的 B 中三元组在 A 相应位置上有非 0 元素,且相加不为 0 ,此时只需要更改 A 中对应位置上的三元组的值即可;
3. 提取到的 B 中三元组在 A 响应位置上有非 0 元素,但相加为 0 ,此时需要删除矩阵 A 中对应结点。
提示:算法中,只需要逐个提取矩阵 B 中的非 0 元素,然后判断矩阵 A 中对应位置上是否有非 0 元素,根据不同的情况,相应作出处理。

2.2 4 种处理

1
2
3
4
5
6
7
8
9
10
设指针 pa 和 pb 分别表示矩阵 A 和矩阵 B 中同一行中的结点( pb 和 pa 都是从两矩阵的第一行的第一个非 0 元素开始遍历),
针对上面的三种情况,细分为 4 种处理过程(第一种情况下有两种不同情况):

1. 当 pa 结点的列值 j > pb 结点的列值 j 或者 pa == NULL (说明矩阵 A 该行没有非 0 元素),
两种情况下是一个结果,就是将 pb 结点插入到矩阵 A 中。

2. 当pa结点的列值j < pb 结点的列值j ,
说明此时 pb 指向的结点位置比较靠后,此时需要移动 pa 的位置,找到离 pb 位置最近的非 0 元素,然后在新的 pa 结点的位置后边插入;
3. 当 pa 的列值 j == pb 的列值 j, 且两结点的值相加结果不为 0 ,只需要更改 pa 指向的结点的值即可;
4. 当 pa 的列值 j == pb 的列值 j ,但是两结点的值相加结果为 0 ,就需要从矩阵 A 的十字链表中删除 pa 指向的结点。

三 实现代码

3.1 AddSMatrix

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
CrossList AddSMatrix(CrossList M,CrossList N) {
OLNode * pa,*pb;//新增的两个用于遍历两个矩阵的结点
OLink * hl=(OLink*)malloc(M.nu*sizeof(OLink));//用于存储当前遍历的行为止以上的区域每一个列的最后一个非 0 元素的位置。
OLNode * pre=NULL;//用于指向 pa 指针所在位置的此行的前一个结点
//遍历初期,首先要对 hl 数组进行初始化,指向每一列的第一个非 0 元素
for (int j=1; j<=M.nu; j++) {
hl[j]=M.chead[j];
}
//按照行进行遍历
for (int i=1; i<=M.mu; i++) {
//遍历每一行以前,都要 pa 指向矩阵 M 当前行的第一个非 0 元素;指针 pb 也是如此,只不过遍历对象为矩阵 N
pa=M.rhead[i];
pb=N.rhead[i];
//当 pb 为 NULL 时,说明矩阵 N 的当前行的非 0 元素已经遍历完。
while (pb!=NULL) {
//创建一个新的结点,每次都要复制一个 pb 结点,但是两个指针域除外。(复制的目的就是排除指针域的干扰)
OLNode * p=(OLNode*)malloc(sizeof(OLNode));
p->i=pb->i;
p->j=pb->j;
p->e=pb->e;
p->down=NULL;
p->right=NULL;
//第一种情况
if (pa==NULL||pa->j>pb->j) {
//如果 pre 为 NULL,说明矩阵 M 此行没有非 0 元素
if (pre==NULL) {
M.rhead[p->i]=p;
} else { //由于程序开始时 pre 肯定为 NULL,所以,pre 指向的是第一个 p 的位置,在后面的遍历过程中,p 指向的位置是逐渐向后移
动的,所有,pre 肯定会在 p 的前边
pre->right=p;
}
p->right=pa;
pre=p;
//在链接好行链表之后,链接到对应列的列链表中的相应位置
if (!M.chead[p->j]||M.chead[p->j]->i>p->i) {
p->down=M.chead[p->j];
M.chead[p->j]=p;
} else {
p->down=hl[p->j]->down;
hl[p->j]->down=p;
}
//更新 hl 中的数据
hl[p->j]=p;
} else {
//第二种情况,只需要移动 pa 的位置,继续判断 pa 和 pb 的位置,一定要有 continue
if (pa->j<pb->j) {
pre=pa;
pa=pa->right;
continue;
}
//第三、四种情况,当行标和列标都想等的情况下,需要讨论两者相加的值的问题
if (pa->j==pb->j) {
pa->e+=pb->e;
//如果为 0,摘除当前结点,并释放所占的空间
if (pa->e==0) {
if (pre==NULL) {
M.rhead[pa->i]=pa->right;
} else {
pre->right=pa->right;
}
p=pa;
pa=pa->right;
if (M.chead[p->j]==p) {
M.chead[p->j]=hl[p->j]=p->down;
} else {
hl[p->j]->down=p->down;
}
free(p);
}
}
}
pb=pb->right;
}
}
//用于输出矩阵三元组的功能函数
display(M);
return M;
}

3.2 完整代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
#include<stdio.h>
#include<stdlib.h>
typedef struct OLNode {
int i,j,e; //矩阵三元组 i 代表行 j 代表列 e 代表当前位置的数据
struct OLNode *right,*down; //指针域 右指针 下指针
} OLNode,*OLink;
typedef struct {
OLink *rhead,*chead; //行和列链表头指针
int mu,nu,tu; //矩阵的行数,列数和非零元的个数
} CrossList;
CrossList CreateMatrix_OL(CrossList M);
CrossList AddSMatrix(CrossList M,CrossList N);
void display(CrossList M);
void main() {
CrossList M,N;
printf("输入测试矩阵 M:\n");
M=CreateMatrix_OL(M);
printf("输入测试矩阵 N:\n");
N=CreateMatrix_OL(N);
M=AddSMatrix(M,N);
printf("矩阵相加的结果为:\n");
display(M);
}
CrossList CreateMatrix_OL(CrossList M) {
int m,n,t;
int i,j,e;
OLNode *p,*q;
scanf("%d%d%d",&m,&n,&t);
M.mu=m;
M.nu=n;
M.tu=t;
if(!(M.rhead=(OLink*)malloc((m+1)*sizeof(OLink)))||!(M.chead=(OLink*)malloc((n+1)*sizeof(OLink)))) {
printf("初始化矩阵失败");
exit(0);
}
for(i=1; i<=m; i++) {
M.rhead[i]=NULL;
}
for(j=1; j<=n; j++) {
M.chead[j]=NULL;
}
for(scanf("%d%d%d",&i,&j,&e); 0!=i; scanf("%d%d%d",&i,&j,&e)) {
if(!(p=(OLNode*)malloc(sizeof(OLNode)))) {
printf("初始化三元组失败");
exit(0);
}
p->i=i;
p->j=j;
p->e=e;
if(NULL==M.rhead[i]||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(NULL==M.chead[j]||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 M;
}
CrossList AddSMatrix(CrossList M,CrossList N) {
OLNode * pa,*pb;
OLink * hl=(OLink*)malloc(M.nu*sizeof(OLink));
OLNode * pre=NULL;
for (int j=1; j<=M.nu; j++) {
hl[j]=M.chead[j];
}
for (int i=1; i<=M.mu; i++) {
pa=M.rhead[i];
pb=N.rhead[i];
while (pb!=NULL) {
OLNode * p=(OLNode*)malloc(sizeof(OLNode));
p->i=pb->i;
p->j=pb->j;
p->e=pb->e;
p->down=NULL;
p->right=NULL;
if (pa==NULL||pa->j>pb->j) {
if (pre==NULL) {
M.rhead[p->i]=p;
} else {
pre->right=p;
}
p->right=pa;
pre=p;
if (!M.chead[p->j]||M.chead[p->j]->i>p->i) {
p->down=M.chead[p->j];
M.chead[p->j]=p;
} else {
p->down=hl[p->j]->down;
hl[p->j]->down=p;
}
hl[p->j]=p;
} else {
if (pa->j<pb->j) {
pre=pa;
pa=pa->right;
continue;
}
if (pa->j==pb->j) {
pa->e+=pb->e;
if (pa->e==0) {
if (pre==NULL) {
M.rhead[pa->i]=pa->right;
} else {
pre->right=pa->right;
}
p=pa;
pa=pa->right;
if (M.chead[p->j]==p) {
M.chead[p->j]=hl[p->j]=p->down;
} else {
hl[p->j]->down=p->down;
}
free(p);
}
}
}
pb=pb->right;
}
}
display(M);
return M;
}
void display(CrossList M) {
printf("输出测试矩阵:\n");
printf("M:\n---------------------\ni\tj\te\n---------------------\n");
for (int i=1; i<=M.nu; i++) {
if (NULL!=M.chead[i]) {
OLink p=M.chead[i];
while (NULL!=p) {
printf("%d\t%d\t%d\n",p->i,p->j,p->e);
p=p->down;
}
}
}
}

运行结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
输入测试矩阵 M:
3 3 3
1 2 1
2 1 1
3 3 1
0 0 0
输入测试矩阵 N:
3 3 4
1 2 -1
1 3 1
2 3 1
3 1 1
0 0 0
矩阵相加的结果为:
输出测试矩阵:
M:
---------------------
i j e
---------------------
2 1 1
3 1 1
1 3 1
2 3 1
3 3 1

四 总结

1
2
使用十字链表法解决稀疏矩阵的压缩存储的同时,在解决矩阵相加的问题中,对于某个单独的结点来说,
算法的时间复杂度为一个常数(全部为选择结构),算法的整体的时间复杂度取决于两矩阵中非 0 元素的个数。

五 参考

  • C语言中文网—十字链表实现矩阵加法