数组的存储结构:一维数组、多维数组都是存放在一个按行优先(也可以按列优先)的一维长数组中。
特殊矩阵(方阵)的压缩存储
对称矩阵(symmetric matrix)
对称矩阵中的元素是按主对角线对称的,即上三角部分和下三角部分元素是对应相等的。
在存储时,一般存储主对角线元素以及下三角部分元素,按行优先。
n阶对称矩阵A可以存储在一维数组B[0..n(n+1)/2-1],一共需存储n(n+1)/2个元素。(数组B下标k从0开始)
一维数组B中 B[k] 与矩阵中元素的 i 和 j 的关系:
上、下三角矩阵(upper/lower triangle matrix)
上(下)三角矩阵指的是下(上)三角部分中的元素均为常数c的方阵。
对于上三角矩阵,存储主对角线元素、上三角部分元素,另外用一个元素存储常数c,按行优先。
n阶上三角矩阵A可以存储在一维数组B[0..n(n+1)/2],一共需存储n(n+1)/2+1个元素。(数组B下标k从0开始)
对于上三角矩阵,一维数组B中 B[k] 与矩阵中元素的 i 和 j 的关系:
对于下三角矩阵,一维数组B中 B[k] 与矩阵中元素的 i 和 j 的关系:
对角矩阵(diagonal matrix)
对角矩阵指的是满足其所有非零元素都集中在以主对角线为中心的带状区域中,即其主对角线上、下方各有b条非零元素构成的次对角线的n阶方阵。
其中,b称为矩阵的半带宽,(2b+1)称为矩阵的带宽。
| i - j |<=b的元素都不为0,其余为0
对角矩阵只存储其非零元素。只有第0行和第n-1行只有两个非零元素,其余行都有3个非零元素。
一维数组B中 B[k] 与矩阵中元素的 i 和 j 的关系:
(可以分别对每行第1个元素、每行第2个元素、每行第3个元素进行讨论得到可得,如下图)
稀疏矩阵(sparse matrix)
当一个阶数较大的矩阵中的非零元素个数 s 相对于矩阵元素的总个数 t 非常小时,即 s<<t 时,称该矩阵为稀疏矩阵。
稀疏矩阵的三元组表示 —— 顺序存储结构
只存储非零元素,为了同时保存非零元素在矩阵中的位置信息,需要同时存储该非零元素的行下标 i 、列下标 j 和元素值。
即每个非零元素由一个三元组唯一确定。
稀疏矩阵的三元组顺序表简称三元组表(list of 3-tuples)
三元组顺序表的数据类型声明
#define M 50 //数值为稀疏矩阵行数
#define N 50 //数值为稀疏矩阵列数
#define MaxSize 60 //数值为稀疏矩阵中非零元素最多的个数
typedef int ElemType;
typedef struct {
int r; //行号
int c; //列号
ElemType d; //元素值
}TupNode;
typedef struct {
int rows; //行数
int cols; //列数
int nums; //非零元素个数
TupNode data[MaxSize]; //最多存放MaxSize个TupeNode类型的数组data,按行优先
}TSMatrix;
从一个二维稀疏矩阵创建其三元组表示
void CreateMat(TSMatrix &t, ElemType A[M][N]) {
t.rows = M;
t.cols = N;
t.nums = 0;
for (int i = 0; i < M; i++) {
for (int j = 0; j < N; j++) {
if (A[i][j] != 0) {
//设置三元组t的data数组中下标为t.nums的TupNode类型结点的行号r
t.data[t.nums].r = i;
t.data[t.nums].c = j;
t.data[t.nums].d = A[i][j];
t.nums++;
}
}
}
}
三元组元素的赋值
bool Value(TSMatrix &t, ElemType x, int i, int j) {
int k=0;
if (i >= t.rows || j >= t.cols) return false;
while (k < t.nums&&t.data[k].r < i) k++;
while (k < t.nums&&t.data[k].c < j) k++;
if (t.data[k].r == i && t.data[k].c == j)
t.data[k].d = x;
else {
for (int k1 = t.nums; k1 > k; k1--) {
t.data[k1].r = t.data[k1 - 1].r;
t.data[k1].c = t.data[k1 - 1].c;
t.data[k1].d = t.data[k1 - 1].d;
}
t.data[k].r = i;
t.data[k].c = j;
t.data[k].d = x;
t.nums++;
}
return true;
}
将指定位置的元素值赋给变量
bool Assign(TSMatrix t, ElemType &x, int i, int j) {
int k = 0;
if (i >= t.rows || j >= t.cols) return false;
while (k < t.nums&&t.data[k].r < i) k++;
while (k < t.nums&&t.data[k].c < j) k++;
if (t.data[k].r == i && t.data[k].c == j)
x = t.data[k].d;
else
x = 0;
return true;
}
输出三元组
void DispMat(TSMatrix t) {
if (t.nums <= 0)
return;
cout << "sparse matrix rows: " << t.rows << " cols: " << t.cols << " non_zero elements: "
<< t.nums << endl<< "------------------------" << endl;
for (int k = 0; k < t.nums; k++)
cout << "(" << t.data[k].r << ", " << t.data[k].c << ", " << t.data[k].d << ")" << endl;
}
稀疏矩阵转置
void TranTat(TSMatrix t, TSMatrix &tb) {
tb.cols = t.rows;
tb.rows = t.cols;
tb.nums = t.nums;
if (t.nums != 0) {
int k1 = 0;
for (int j = 0; j < t.cols; j++) {
for (int k = 0; k < t.nums; k++) {
if (t.data[k].c == j) {
tb.data[k1].r = t.data[k].c;
tb.data[k1].c = t.data[k].r;
tb.data[k1].d = t.data[k].d;
k1++;
}
}
}
}
}
稀疏矩阵的十字链表(orthogonal list)表示 —— 链式存储结构
创建稀疏矩阵的十字链表的步骤:
1)对于稀疏矩阵中每个非零元素创建一个结点存放它,包含元素的行号、列号和元素值
2)将同一行的所有结点构成一个带头结点的循环单链表,行号为 i 的单链表的头结点为 hr[i]。
hr[i] 头结点的行指针指向行号为 i 的单链表的首结点
3)将同一列的所有结点构成一个带头结点的循环单链表,列号为 j 的单链表的头结点为 hd[j]。
hd[j] 头结点的列指针指向列号为 j 的单链表的首结点
4)事实上,可以将hr[i] 和 hd[j]合起来变为h[i], 即h[i]同时包含行指针和列指针。
h[i] 头结点的行指针指向行号为 i 的单链表的首结点,h[i] 头结点的列指针指向列号为 i 的单链表的首结点。
(h[i]个数取行数与列数中较大者)
5)再将所有头结点h[i] 连起来构成一个带头结点的循环单链表,头结点为hm
6)最后在总头结点hm 中存放稀疏矩阵的行数和列数等信息。
一个稀疏矩阵的十字链表,如图:
稀疏矩阵的十字链表的结点类型MatNode声明
typedef int ElemType;
typedef struct mtxn {
int row;
int col;
struct mxtn *right, *down;
union {
ElemType value;
struct mxtn* link;
}tag;
}MatNode;
通过头结点h[i] 的h[i]->right 指针可以逐行搜索行下标为i 的所有非零元素,h[i]->down指针可以逐列搜索列下标为i 的所有非零元素。每一个非零元素同时包含在两个链表中,方便运算中行方向和列方向的搜索,因而降低了算法的时间复杂度。(不用按k逐一遍历)
稀疏矩阵十字链表的运算算法设计比较复杂,不再赘述