矩阵之间能够进行加法运算的前提条件是:各矩阵的行数和列数必须相等。
在行数和列数都相等的情况下,矩阵相加的结果就是矩阵中对应位置的值相加所组成的矩阵,例如:
采用链式存储结构存储稀疏矩阵三元组的方法,称为“十字链表法”。
用结构体自定义表示为:
所以,采用结构体自定义十字链表的结构,为:
对应行链表的位置确定之后,判断数据元素 A 在对应列的位置:
实现代码:
图1 矩阵相加
十字链表法
之前所介绍的都是采用顺序存储结构存储三元组,在类似于矩阵的加法运算中,矩阵中的数据元素变化较大(这里的变化主要为:非0元素变为0,0变为非0元素),就需要考虑采用另一种结构——链式存储结构来存储三元组。采用链式存储结构存储稀疏矩阵三元组的方法,称为“十字链表法”。
十字链表法表示矩阵
例如,用十字链表法表示矩阵 A ,为:
图2 矩阵用十字链表法表示
由此可见,采用十字链表表示矩阵时,矩阵的每一行和每一个列都可以看作是一个单独的链表,而之所以能够表示矩阵,是因为行链表和列链表都分别存储在各自的数组中
图 2 中:存储行链表的数组称为 rhead 数组;存储列链表的数组称为 chead 数组。
十字链表中的结点
从图2中的十字链表表示矩阵的例子可以看到,十字链表中的结点由 5 部分组成:
图3 十字链表中的结点
指针域A存储的是矩阵中结点所在列的下一个结点的地址(称为 “down域”);
指针域B存储的是矩阵中该结点所在行的下一个结点的地址(称为 “right域”);
指针域B存储的是矩阵中该结点所在行的下一个结点的地址(称为 “right域”);
用结构体自定义表示为:
typedef struct OLNode { int i, j, e; //矩阵三元组 i 代表行 j 代表列 e 代表当前位置的数据 struct OLNode *right, *down; //指针域 右指针 下指针 }OLNode, *OLink;
十字链表的结构
使用十字链表表示一个完整的矩阵,在了解矩阵中各结点的结构外,还需要存储矩阵的行数、列数以及非 0 元素的个数,另外,还需要将各结点链接成的链表存储在数组中。所以,采用结构体自定义十字链表的结构,为:
typedef struct { OLink *rhead, *chead; //存放各行和列链表头指针的数组 int mu, nu, tu; //矩阵的行数,列数和非零元的个数 }CrossList;
十字链表存储矩阵三元组
由于三元组存储的是该数据元素的行标、列标和数值,所以,通过行标和列标,就能在十字链表中唯一确定一个位置。首先判断该数据元素 A(例如三元组为:(i,j,k))所在行的具体位置:判断方法为:在同一行中通过列标来判断位置;在同一列中通过行标来判断位置。
- 如果 A 的列标 j 值比该行第一个非 0 元素 B 的 j 值小,说明该数据元素在元素 B 的左侧,这时 A 就成为了该行第一个非0元素(也适用于当该行没有非 0 元素的情况,可以一并讨论)
- 如果 A 的列标 j 比该行第一个非 0 元素 B 的 j 值大,说明 A 在 B 的右侧,这时,就需要遍历该行链表,找到插入位置的前一个结点,进行插入。
对应行链表的位置确定之后,判断数据元素 A 在对应列的位置:
- 如果 A 的行标比该列第一个非 0 元素 B 的行标 i 值还小,说明 A 在 B 的上边,这时 A 就成了该列第一个非 0 元素。(也适用于该列没有非 0 元素的情况)
- 反之,说明 A 在 B 的下边,这时就需要遍历该列链表,找到要插入位置的上一个数据元素,进行插入。
实现代码:
//创建系数矩阵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))