基础知识
在定义稀疏矩阵时,可以引入一个称为稀疏因子的数,其定义如下:假设对于一个 $m*n$ 矩阵,其中有 $t$ 个元素不为 $0$, 则定义稀疏因子 $δ=t/(m*n)$,一般认为 $δ<=0.05$ 的矩阵为稀疏矩阵。稀疏矩阵如果采用二维数组直接存储将会导致大量的空间存储 $0$ 值,对于空间是一种极大的浪费,因此一般采用压缩存储,目前稀疏矩阵常用三元组顺序表和十字链表来存储,本文将实现采用三元组顺序表压缩稀疏矩阵并实现矩阵常用的操作。
在实现三元组顺序表压缩存储稀疏矩阵之前,预先定义三元组节点结构如下:
1 | template<typename DataType> |
需要是实现的矩阵基本运算主要有:
- 矩阵转置:TransposeMatrix
- 矩阵加法:AddMatrix
- 矩阵减法:SubMatrix
- 矩阵乘法:MultiMatrix
- 打印矩阵:PrintMatrix
三元组顺序表基本定义
根据上述分析可定义如下基本ADT:
1 |
|
如上即为整个三元组顺序表的ADT基本结构,具体含义可见注释。接下来依次实现个函数。
三元组顺序表ADT的实现
- 构造函数
构造函数主要将二维存储的稀疏矩阵转变为三元组形式存储的矩阵。
1 | template<typename DataType> |
- 析构函数
析构主要释放rowFirstNoneZeroPos申请的空间,实现如下所示:
1 | template<typename DataType> |
- 常规矩阵转置
由于三元组顺序表的存储顺序是行优先依次存储,而转置后的三元组行号变成列号,原始行号有序使得转置后的列号有序,因此原始三元组转置后的一行中的列相对顺序是正确的。故此有如下代码:
1 |
|
- 快速矩阵转置
由于在常规转置中,我们需要一行一行来处理,如果每遍历到一个三元组便知道该元素放入的位置,那么可以极大降低时间复杂度,此时我们需要计算一个转置后每行起始的位置,然后在遍历过程中可以快速填写三元组顺序表,具体实现代码如下所示:
1 |
|
上述代码增加辅助空间后便将时间复杂度降低了一个量级。
- 矩阵相加
在进行矩阵加法,首先需要判断两个矩阵是否能够相加,接着使用双下标指针分别指向第一个矩阵和第二个矩阵,如果两个指向的行列号相等只需直接相加,否则需要判断行列号大小,具体实现代码如下:
1 | template<typename DataType> |
- 矩阵减法
矩阵减法和矩阵加法类似,具体代码如下所示:
1 | template<typename DataType> |
- 矩阵乘法
矩阵乘法相对而言比较复杂,矩阵乘法如果偷懒可以先解压缩然后使用常规乘法进行计算最后再把结果进行压缩,但这样就不能体现三元组的作用了,因此需要找出一种可以直接在三元组上进行计算的乘法算法。考虑到矩阵乘法的实质是从第一个矩阵抽出一个行向量和从第二个矩阵抽出一个列向量并做向量的点乘,其中行、列号分别对应在结果矩阵中的行、列号,综合上述分析我们可以一行一行处理,其首先也需要判断运算是否合法,具体代码如下所示:
1 | template<typename DataType> |
- 压缩矩阵转成二维数组
直接遍历即可,不存在意味着是零值。
1 | template<typename DataType> |
- 打印矩阵
先调用转二维函数然后直接打印即可,代码如下:
1 |
|
- 计算每行元素的第一个下标值
这在快速矩阵转置中实现过类似的思路,直接把代码稍作修改即可,结果见下:
1 | template<typename DataType> |
代码测试
通过随机生成两个稀疏矩阵,然后依次测试各个函数是否正确,测试代码如下所示:
1 |
|