5.矩阵的压缩储存
5.1 特殊矩阵
- n阶对称矩阵
按行序为主序,一维数组 s z [ n ( n + 1 ) / 2 ] sz[n(n+1)/2] sz[n(n+1)/2]作为储存结构
a = [ a 11 a 12 . . . a 1 n a 21 a 22 . . . a 2 n . . . . . a n 1 a n 2 . . . a n n ] k = { i ( i − 1 ) / 2 + j − 1 , if i >=j j ( j − 1 ) / 2 + i − 1 , if i <j a = \left[ \begin{matrix} a_{11} & a_{12} & ... & a_{1n} \\ a_{21} & a_{22} & ... & a_{2n} \\ & .....\\ a_{n1} & a_{n2} & ... & a_{nn} \end{matrix} \right]\\ \\ k = \begin{cases} i(i-1)/2+j-1, & \text{if }i\text{ >=j} \\ j(j-1)/2+i-1, & \text{if }i\text{<j} \end{cases} a=⎣⎢⎢⎡a11a21an1a12a22.....an2.........a1na2nann⎦⎥⎥⎤k={i(i−1)/2+j−1,j(j−1)/2+i−1,if i >=jif i<j - 下三角矩阵
a = [ a 11 0 . . . 0 a 21 a 22 . . . 0 . . . . . 0 a n 1 a n 2 . . . a n n ] k = { i ( i − 1 ) / 2 + j − 1 , if i >=j j ( j − 1 ) / 2 + i − 1 , if i <j a = \left[ \begin{matrix} a_{11} & 0 & ... & 0 \\ a_{21} & a_{22} & ... & 0 \\ & & .....&0\\ a_{n1} & a_{n2} & ... & a_{nn} \end{matrix} \right]\\ \\ k = \begin{cases} i(i-1)/2+j-1, & \text{if }i\text{ >=j} \\ j(j-1)/2+i-1, & \text{if }i\text{<j} \end{cases} a=⎣⎢⎢⎡a11a21an10a22an2..............000ann⎦⎥⎥⎤k={i(i−1)/2+j−1,j(j−1)/2+i−1,if i >=jif i<j
- 对角矩阵
a = [ a 11 a 12 0 . . . . . . 0 a 21 a 22 a 23 . . . . . . 0 0 a 32 a 33 a 34 . . . 0 . . . . . 0 0 . . . a n − 1 , n − 2 a n − 1 , n − 1 a n − 1 , n 0 0 . . . 0 a n , n − 1 a n n ] L o c ( a i j ) = L o c ( a 11 ) + [ 2 ( i − 1 ) + ( j − 1 ) ] ∗ l a = \left[ \begin{matrix} a_{11} & a_{12} & 0 &... &... & 0 \\ a_{21} & a_{22} & a_{23} &... &... & 0 \\ 0 & a_{32} & a_{33} & a_{34} & ... & 0 \\ & &&&& .....\\ 0 &0&... & a_{n-1,n-2} & a_{n-1,n-1} & a_{n-1,n}\\ 0 & 0 & ... & 0 &a_{n,n-1} & a_{nn} \end{matrix} \right]\\ \\ Loc(a_{ij}) = Loc(a_{11})+[2(i-1)+(j-1)]*l a=⎣⎢⎢⎢⎢⎢⎢⎡a11a21000a12a22a32000a23a33............a34an−1,n−20.........an−1,n−1an,n−1000.....an−1,nann⎦⎥⎥⎥⎥⎥⎥⎤Loc(aij)=Loc(a11)+[2(i−1)+(j−1)]∗l
5.2稀疏矩阵
5.2.1顺序储存
- 三元组顺序表
typedef int ElemType;
/* 三元组类型定义,主要用来存储非零元 */
typedef struct {
int i, j; // 该三元组非零元的行下标和列下标
ElemType e;
} Triple;
/* 三元组稀疏矩阵类型定义 */
typedef struct {
Triple data[MAXSIZE + 1]; // 非零元三元组表,data[0]未用
int mu, nu, tu; // 矩阵的行数、列数和非零元个数
} TSMatrix;
- 逻辑链接的稀疏矩阵
/* 行逻辑链接的稀疏矩阵类型定义 */
typedef struct {
Triple data[MAXSIZE + 1]; // 非零元三元组表,data[0]未用
int rpos[MAXRC + 1]; // 各行第一个非零元在三元组表中的位置表,rpos[0]未用
int mu, nu, tu; // 矩阵的行数、列数和非零元个数
} RLSMatrix;
- 伪地址
伪地址的运用于考察不多,故不做介绍
5.2_(*)矩阵转置
- 方法1 按M的列序转置
//算法5.1
Status TransposeSMatrix(TSMatrix M, TSMatrix* T) {
int p, q, col;
(*T).mu = M.nu;
(*T).nu = M.mu;
(*T).tu = M.tu;
if((*T).tu != 0) {
q = 1; // q用于T中非零元的计数
// col代表M的列,T的行
for(col = 1; col <= M.nu; ++col) {
// 在M中查找第j列的元素,依次将其转置到T中
for(p = 1; p <= M.tu; ++p) {
if(M.data[p].j == col) {
(*T).data[q].i = M.data[p].j; // M的列变为T的行
(*T).data[q].j = M.data[p].i; // M的行变为T的列
(*T).data[q].e = M.data[p].e; // 每个三元组值不变
++q;
}
}
}
}
return OK;
}
- 方法2 快速转置
//算法5.2
Status FastTransposeSMatrix(TSMatrix M, TSMatrix* T) {
int col, t, p, q;
int* num; // num[col] 表示M第col列中非零元的个数
int* copt; // copt[col]表示M第col列第一个非零元在转置后矩阵中的位置
(*T).mu = M.nu;
(*T).nu = M.mu;
(*T).tu = M.tu;
// 提前返回
if((*T).tu == 0) {
return ERROR;
}
num = (int*) malloc((M.nu + 1) * sizeof(int));
copt = (int*) malloc((M.nu + 1) * sizeof(int));
// 初始化数组num
for(col = 1; col <= M.nu; ++col) {
num[col] = 0;
}
// 统计M中的非零元,统计每列非零元的个数
for(t = 1; t <= M.tu; ++t) {
num[M.data[t].j]++;
}
// 第1列第1个非零元总是位于转置后矩阵中的首位
copt[1] = 1;
// 计算各列第1个非零元在转置矩阵中的位置
for(col = 2; col <= M.nu; ++col) {
copt[col] = copt[col - 1] + num[col - 1];
}
// 依次扫描M中的三元组
for(p = 1; p <= M.tu; ++p) {
col = M.data[p].j; // 计算当前非零元所处的列
q = copt[col]; // 计算当前非零元在转置矩阵中的位置
(*T).data[q].i = M.data[p].j;
(*T).data[q].j = M.data[p].i;
(*T).data[q].e = M.data[p].e;
++copt[col]; // 再遇到此列元素时,其在转置矩阵中的位置应当增一(该步骤很重要)
}
return OK;
}
5.2.2 链式储存
- 带行指针的单链表
- 每行的非零元用一个单链表存放
- 设置一个行指针数组,指向本行第一个非零元结点;若本行无非零元,则指针为空
- 数据结构定义方法:
typedef struct node
{ int col;
int val;
struct node *link;
}JD;
typedef struct node *TD
- 十字链表
设行指针数组和列指针数组,分别指向每行、列第一个非零元
/* 非零元类型定义 */
typedef struct OLNode {
int i, j; // 该非零元的行下标和列下标
ElemType e;
struct OLNode* right; // 该非零元所在的行表的后继链域
struct OLNode* down; // 该非零元所在的列表的后继链域
} OLNode, * OLink;
/* 十字链表类型定义 */
typedef struct {
OLink* rhead; // 行链表头指针
OLink* chead; // 列链表头指针
int mu, nu, tu; // 矩阵的行数、列数和非零元个数
} CrossList;
// 算法5.4:如何建立十字链表
Status CreateSMatrix(CrossList* M, char* path) {
int i, j, k;
OLNode* p, * q;
FILE* fp;
printf("请输入行数:");
scanf("%d", &((*M).mu));
printf("请输入列数:");
scanf("%d", &((*M).nu));
printf("请输入非零元素个数:");
scanf("%d", &((*M).tu));
printf("请输入%d个三元组信息\n", (*M).tu);
// 创建行链(类似行索引,0号单元弃用)
(*M).rhead = (OLink*) malloc(((*M).mu + 1) * sizeof(OLink));
if((*M).rhead == NULL) {
exit(OVERFLOW);
}
// 创建列链(类似列索引,0号单元弃用)
(*M).chead = (OLink*) malloc(((*M).nu + 1) * sizeof(OLink));
if((*M).chead == NULL) {
exit(OVERFLOW);
}
// 初始化行链索引为NULL
for(k = 0; k <= (*M).mu; ++k) {
(*M).rhead[k] = NULL;
}
// 初始化列链索引为NULL
for(k = 0; k <= (*M).nu; ++k) {
(*M).chead[k] = NULL;
}
// 依次录入非零元
for(k = 1; k <= (*M).tu; ++k) {
// 创建三元组结点
p = (OLNode*) malloc(sizeof(OLNode));
if(p == NULL) {
exit(OVERFLOW);
}
printf("第%2d组:", k);
scanf("%d%d%d", &i, &j, &(p->e));
p->i = i; // 行号
p->j = j; // 列号
p->right = p->down = NULL;
// 开始行的插入
// 如果该行还没有元素,或已有元素均位于该元素右侧,则可以直接插入
if((*M).rhead[i] == NULL || (*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) {
}
if(q->j == p->j || ((q->right) && q->right->j == p->j)) {
printf("此位置已被占用!!\n");
exit(ERROR);
}
p->right = q->right;
q->right = p;
}
/*
* 开始列的插入
*/
// 如果该列还没有元素,或已有元素均位于该元素下侧,则可以直接插入
if((*M).chead[j] == NULL || (*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) {
}
if(q->i == p->i || ((q->down) && q->down->i == p->i)) {
printf("此位置已被占用!!\n");
exit(ERROR);
}
p->down = q->down;
q->down = p;
}
}
return OK;
}
5.3广义表
L S = ( α 1 , α 2 , α 3 , . . . , α n ) LS = (\alpha_1,\alpha_2,\alpha_3,...,\alpha_n) LS=(α1,α2,α3,...,αn),其中 α i \alpha_i αi可以是单个元素也可以是广义表,一般而言, α 1 \alpha_1 α1为表头, α n \alpha_n αn为表尾
- 任何一个非空列表其表头可能是原子,也可能是列表.而其表尾必定为列表。
- 习惯上,用大写字母表示广义表的名称,用小写字母表示原子。当广义表LS非空时,称第一个元素a1为LS的表头(Head),称其余元素组成的表为LS的表尾(Tail)。
- 值得提醒的是列表()和(())不同。前者为空表.长度n=0;后者长度n=1,可分解得到其表头、表尾均为空表()
- (())与()是两个不同的列表,前者长度为1,表头与表尾均为空表,后者为空表,长度为0
- 如:A = (),B = (e),C = (a,(b,c,d)),D = (A,B,C)
- 储存结构:
- 链式储存结构,每个数据元素用一个节点表示
//Atom-0:原子结点 List-1:表结点
typedef enum { Atom, List } ElemTag;
typedef struct GLNode {
ElemTag tag; // 公共标记,用于区分原子结点和表结点
union {// 原子结点和表结点的联合部分
AtomType atom; // atom是原子结点的值域,AtomType由用户定义
struct {
struct GLNode* hp; // 指向表头
struct GLNode* tp; // 指向表尾
} ptr; // 表结点的指针域
} Node;
} GLNode;
/* 广义表类型 */
typedef GLNode* GList;
- 扩展的线性表表示
typedef enum { Atom, List } ElemTag;
/* 广义表(扩展线性链表存储表示)类型定义 */
typedef struct GLNode {
ElemTag tag; // 公共标记,用于区分原子结点和表结点
// 原子结点和表结点的联合部分
union
{
AtomType atom; // atom是原子结点的值域,AtomType由用户定义
struct GLNode* hp; // 指向表头
} Node;
struct GLNode* tp; // 指向表尾
} GLNode;
typedef GLNode* GList;//广义表类型
本文为华中科技大学(WuHan China)人工智能与自动化学院 2020级《数据结构与算法分析》课堂所做的个人笔记,结课之余慢慢更新,未来将继续更新《数据结构习题集(C语言版)》解析以及所用代码等。
当然,如果你会使用git工具,也可以直接在作者的Github个人主页直接下载学习
本文相关链接:作者的Github相关主页
由于作者才疏学浅,文章有错误之处请私信或发邮件(szh-hust@outlook.com)指正