在数值计算与数据结构领域,稀疏矩阵(非零元素占比极低的矩阵)的高效存储是一项关键课题。若用常规二维数组存储,大量 “0” 元素会造成空间与性能的双重浪费。为此,三元组表(顺序存储)与十字链表(链式存储) 两种经典结构应运而生,它们从不同角度为稀疏矩阵的存储与操作提供了最优解。本文将深入剖析这两种结构的设计逻辑、实现细节与适用场景。
一、稀疏矩阵:存储优化的必要性
假设一个 \(m \times n\) 的矩阵中,非零元素数量远小于 \(m \times n\)(如仅占 1% 甚至更低),这类矩阵即为稀疏矩阵。例如,电路节点的连接矩阵、大规模社交网络的邻接矩阵等,都具有稀疏性。
若用普通二维数组存储,不仅会浪费大量内存(“0” 元素无意义却占据空间),还会降低算法效率(遍历或运算时需频繁处理无意义的 “0”)。因此,针对稀疏矩阵的 “定制化” 存储结构成为必然。
二、顺序存储:三元组表的 “紧凑哲学”
1. 核心设计:用三元组聚焦非零元素
三元组表的本质是以顺序表存储稀疏矩阵的所有非零元素,每个非零元素用一个 ** 三元组(行号 i、列号 j、值 v)** 描述。为了全局管理,表的开头还会记录矩阵的总行数、总列数和非零元素总数。
2. 结构定义(以 C 语言为例)
c
运行
// 单个非零元素的三元组
typedef struct {
int row; // 行号
int col; // 列号
ElemType val; // 非零元素的值
} Triple;
// 三元组表的整体结构
typedef struct {
Triple data[MAX_SIZE]; // 存储三元组的数组
int rows; // 矩阵总行数
int cols; // 矩阵总列数
int num; // 非零元素总数
} TSMatrix;
3. 实例演示:从矩阵到三元组表
考虑如下稀疏矩阵:\(\begin{bmatrix} 5 & 0 & 0 & 7 \\ 0 & 0 & -3 & 0 \\ 0 & 0 & 0 & 0 \\ 0 & 2 & 0 & 0 \\ \end{bmatrix}\)其对应的三元组表为:
- 矩阵信息:
rows=4,cols=4,num=4 - 三元组数据:
(0, 0, 5)、(0, 3, 7)、(1, 2, -3)、(3, 1, 2)
4. 优缺点剖析
- 优势:存储极其紧凑,空间利用率高;基于数组的顺序遍历效率优异。
- 局限:插入、删除非零元素时需大规模移动数组元素,时间成本高;仅适用于 “静态” 稀疏矩阵(非零元素数量与位置变化少)。
三、链式存储:十字链表的 “灵活美学”
1. 核心设计:十字交叉的双向链表
十字链表是一种链式结构,为稀疏矩阵的每一行和每一列都维护了一条链表。每个非零元素节点同时属于所在行的链表和所在列的链表,形成 “十字交叉” 的链式网络,从而实现行、列两个维度的高效操作。
2. 节点与整体结构(以 C 语言为例)
c
运行
// 非零元素节点
typedef struct OLNode {
int row; // 行号
int col; // 列号
ElemType val; // 非零元素的值
struct OLNode *right; // 指向同行下一个非零元素
struct OLNode *down; // 指向同列下一个非零元素
} OLNode, *OLink;
// 十字链表的整体结构
typedef struct {
OLink *row_head; // 行头节点数组(每个元素指向对应行的第一个非零节点)
OLink *col_head; // 列头节点数组(每个元素指向对应列的第一个非零节点)
int rows; // 矩阵总行数
int cols; // 矩阵总列数
int num; // 非零元素总数
} CrossList;
3. 实例演示:可视化十字链表
以之前的稀疏矩阵为例,十字链表的结构可简化为:
- 行链表:
row_head[0]指向(0,0,5),再指向(0,3,7);row_head[1]指向(1,2,-3);row_head[3]指向(3,1,2)(行 2 无节点)。 - 列链表:
col_head[0]指向(0,0,5);col_head[1]指向(3,1,2);col_head[2]指向(1,2,-3);col_head[3]指向(0,3,7)。 - 每个非零节点的
right和down指针分别串联同行、同列的下一个节点,形成十字网络。
4. 优缺点剖析
- 优势:插入、删除非零元素时仅需修改链表指针,操作灵活高效;适合稀疏矩阵的动态运算(如矩阵加法、乘法)。
- 局限:每个节点需额外存储两个指针,空间开销略高于三元组表;链表遍历的缓存局部性较差,大规模遍历时效率不如顺序存储。
四、深度对比:三元组表 vs 十字链表
| 对比维度 | 三元组表(顺序存储) | 十字链表(链式存储) |
|---|---|---|
| 存储结构 | 数组(顺序) | 链表(链式) |
| 空间复杂度 | \(O(n)\)(n 为非零元素数) | \(O(n + rows + cols)\)(额外存储行 / 列头节点与指针) |
| 插入 / 删除效率 | 低(需移动数组元素) | 高(仅修改指针) |
| 遍历效率 | 高(数组顺序访问) | 中(链表指针跳转) |
| 适用场景 | 静态稀疏矩阵(少修改) | 动态稀疏矩阵(频繁插入 / 删除、矩阵运算) |
五、场景化决策:如何选择?
- 若你面对的是结构稳定、几乎无修改的稀疏矩阵(如预处理后的图邻接矩阵),三元组表是更优解 —— 空间紧凑,遍历速度快。
- 若你需要对稀疏矩阵进行频繁的插入、删除操作,或开展矩阵加法、乘法等动态运算,十字链表的灵活性将大幅降低时间成本。
六、总结
三元组表与十字链表是稀疏矩阵存储的 “双璧”,前者以 “顺序紧凑” 实现空间最优,后者以 “链式灵活” 实现操作最优。理解二者的设计逻辑与适用边界,是高效处理稀疏矩阵类问题的关键。
在数据结构的世界里,从来没有 “一劳永逸” 的完美方案,只有 “因地制宜” 的智慧选择 —— 这正是稀疏矩阵存储艺术带给我们的深层启示。
1万+

被折叠的 条评论
为什么被折叠?



