一个宝藏视频【数据结构】特殊矩阵的压缩存储/对称矩阵/三角矩阵/对角矩阵(含经典题讲解)_哔哩哔哩_bilibili
一、二维数组的顺序存储结构
(一)行主序
1.顺序: a11,a12,...,a1n,a21,a22...,从a11到amn,存在下标为0~mn-1的一维数组里,一行一行存
2.特点: 有地址计算公式,可以随机访问
3.地址: Loc(aij)=Loc(a11) + [(i-1)n+(j-1)] * L,Loc(a11)是基地址
(二)列主序
1.顺序: a11,a21,...,am1,a12,a22...,从a11到amn,存在下标为0~mn-1的一维数组里,一列一列存
2.特点: 有地址计算公式,可以随机访问
3.地址: Loc(aij)=Loc(a11) + [(j-1)m+(i-1)] * L,Loc(a11)是基地址
二、矩阵(二维数组)的压缩存储
宗旨: 为值相同的矩阵元素只分配一个空间,对零元不分配存储空间
(一)特殊矩阵(非零元分布有规则)
1.上三角矩阵
特点: i>j时,aij = 0或同一个常量c
(1)列主序
若下半三角为0:
Loc(aij)=Loc(a11)+((j-1)j/2+i-1) * L
a11存在sa[0],地址为0,L=1,aij存在的元素下标为k=(j-1)j/2+i-1
数组大小M=n(n+1)/2
若下半三角为c:
M=n(n+1)/2+1
c=sa[n(n+a)/2-1],c的地址是n(n+1)/2
Loc(aij)=(j-1)j/2+i-1
2.下三角矩阵
(1)行主序
若上半三角为0:
Loc(aij)=Loc(a11)+((i-1)i/2+j-1) * L
a11存在sa[0],地址为0,L=1,aij存在的元素下标为k=(i-1)i/2+j-1
数组大小M=n(n+1)/2
若上半三角为c:
M=n(n+1)/2+1
c=sa[n(n+a)/2-1],c的地址是n(n+1)/2
Loc(aij)=(i-1)i/2+j-1
2.对称矩阵
特点: aij=aji
方式: 只存上三角,或者只存下三角
3.2d+1-对角矩阵
行主序
特点:主对角线,主对角线上面d条对角线,主对角线下面d条对角线,这三条线上的元素分布不规律,非0或非c
方式:第一行和最后一行,各存d+1个元素,余下每行存2d+1个元素
地址:
矩阵元素下标从0开始
Loc(aij)=Loc(a00)+(2d+1)i-d+j-(i-d);(i>=0,j<=n-1,|i-j|<=d)
矩阵元素下标从1开始
Loc(aij)=Loc(a11)+(2d+1)(i-1)-d+j-i+d;(i>=1,j<=n,|i-j|<=d)
(二)稀疏矩阵(零元多,分布无规则)
m行n列矩阵,t个非零元,稀疏因子δ=t/(mn)<=0.05,为稀疏矩阵
原则:只储存每个非零元的行、列下标,其值,矩阵的行列维数
1.三元组顺序表
- 所需存储单元个数为3(ma.tu+1)
- 行主序,行内按列序
//三元组结构
typedef struct{
int i,j;//非零元的行、列下标
int e;//非零元的值
}Triple;
//稀疏矩阵结构
#define MAXSIZE 100//非零元最大个数
typedef struct{
Triple data[MAXSIZE+1];//三元组表,data[0]未用,或存放矩阵行数、列数、非零元个数
int mu, nu, tu;//矩阵行数、列数、非零元个数
}TSMatrix;
如何已知矩阵的三元组表M,求转置矩阵的三元组表T?
//一般矩阵算法
//时间复杂度T(n)=O(mu*nu)
for(col=0;col<nu;col++)
for(row=0;row<mu;row++)
T[col][row]=M[row][col];
//按M的列序转置,效率低,T(n)=O(M.nu * M.tu)
//三元组 法一 将每个三元组的ij互换,再重排新表,使之为行主序(即原表的列主序)
int TransposeSMatrix(TSMatrix M, TSMatrix &T){
int col,p,k;
T.mu = mu.tu;
T.nu = M.mu;
T.tu = M.tu;
if(T.tu) {//若有非零元,则转置
k = 1;//表示T.data表的下标
for(col=1; col<=M.nu; col++)//查找M每一列的非零元
for(p=1; p<=M.tu;p++)//扫描M所有非零元
if(M.data[p].j == col){
T.data[k].i = M.data[p].j;
T.data[k].j = M.data[p].i;
T.data[k].e = M.data[p].e;
k++;
}
return 1;//成功
}
return -1;//失败
}
有一种方法可以解决问题,那就是用空间(增加辅助向量)来换时间,接下来我们看快速转置。
void fast_transpos(TSMatrix M, TSMatrix &T) {
int col,p,k;
int num[N];//矩阵M中第col列中非零元的个数
int cpot[N];//矩阵M中第col列第一个非零元在T.data中的位置,第一个位置是1
T.mu = M.tu;
T.nu = M.mu;
T.tu = M.tu;
if(T.tu) {
//求num
for(col=1;col<M.nu;col++) num[col] = 0;
for(p=1; p<=M.tu;p++) num[M.data[p].j]++;
//求cpot
cpot[1]=1;
for(col=2;col<M.nu;col++)
cpot[col] = cpot[col-1]+num[col-1];
//转置
for(p=1; p <= M.tu; p++) {
col = M.data[p].j;
k=cpot[col];
T.data[k].i = M.data[p].j;
T.data[k].j = M.data[p].i;
T.data[k].e = M.data[p].e;
++M.data[p].j;
}
}
return;
}
验证以上方法的可调试代码:
#include<stdio.h>
#include<stdlib.h>
#define N 100
//三元组结构
typedef struct{
int i,j;//非零元的行、列下标
int e;//非零元的值
}Triple;
//稀疏矩阵结构
#define MAXSIZE 100//非零元最大个数
typedef struct{
Triple data[MAXSIZE+1];//三元组表,data[0]未用,或存放矩阵行数、列数、非零元个数
int mu, nu, tu;//矩阵行数、列数、非零元个数
}TSMatrix;
void fast_transpos(TSMatrix M, TSMatrix &T) {
int col,p,k;
int num[N];//矩阵M中第col列中非零元的个数
int cpot[N];//矩阵M中第col列第一个非零元在T.data中的位置,第一个位置是1
T.mu = M.tu;
T.nu = M.mu;
T.tu = M.tu;
if(T.tu) {
//求num
for(col=1;col<=M.nu;col++) num[col] = 0;
for(p=1; p<=M.tu;p++) num[M.data[p].j]++;
//求cpot
cpot[1]=1;
cpot[0]=1;
for(col=2;col<=M.nu;col++)
cpot[col] = cpot[col-1]+num[col-1];
//转置
for(p=1; p <= M.tu; p++) {
col = M.data[p].j;
k=cpot[col];
T.data[k].i = M.data[p].j;
T.data[k].j = M.data[p].i;
T.data[k].e = M.data[p].e;
++cpot[col];//易错点,注意
}
}
return;
}
void TransposeSMatrix(TSMatrix M, TSMatrix &T){
int col,p,k;
T.mu = M.tu;
T.nu = M.mu;
T.tu = M.tu;
if(T.tu) {//若有非零元,则转置
k = 1;//表示T.data表的下标
for(col=1; col<=M.nu; col++)//查找M每一列的非零元
for(p=1; p<=M.tu;p++)//扫描M所有非零元
if(M.data[p].j == col){
T.data[k].i = M.data[p].j;
T.data[k].j = M.data[p].i;
T.data[k].e = M.data[p].e;
k++;
}
}
return ;//失败
}
int main(){
TSMatrix M, T1, T2;
// 初始化稀疏矩阵M
M.mu = 3;
M.nu = 4;
M.tu = 5;
M.data[1].i = 1; M.data[1].j = 1; M.data[1].e = 3;
M.data[2].i = 1; M.data[2].j = 4; M.data[2].e = 5;
M.data[3].i = 2; M.data[3].j = 2; M.data[3].e = 1;
M.data[4].i = 3; M.data[4].j = 1; M.data[4].e = 2;
M.data[5].i = 3; M.data[5].j = 3; M.data[5].e = 4;
printf("\n原三元表:\n");
for (int i = 1; i <= M.tu; i++) {
printf("(%d, %d, %d)\n", M.data[i].i, M.data[i].j, M.data[i].e);
}
// 验证按列序转置算法
TransposeSMatrix(M, T1);
// 输出按列序转置后的矩阵
printf("按列序转置后的三元表:\n");
for (int i = 1; i <= T1.tu; i++) {
printf("(%d, %d, %d)\n", T1.data[i].i, T1.data[i].j, T1.data[i].e);
}
// 验证快速转置算法
fast_transpos(M, T2);
// 输出快速转置后的矩阵
printf("\n快速转置后的三元表:\n");
for (int i = 1; i <= T2.tu; i++) {
printf("(%d, %d, %d)\n", T2.data[i].i, T2.data[i].j, T2.data[i].e);
}
return 0;
}
2.行逻辑联接的顺序表
特点:可以随机存取某一行中的非零元
#define MAXSIZE 100
//三元组结构
typedef struct{
int i,j;//非零元的行、列下标
int e;//非零元的值
}Triple;
#define MAXRC 500
//行逻辑链接顺序表类型
typedef struct{
Triple data[MAXSIZE+1];//从data[1]开始存放
int rpos[MAXRC+1];//每一行非零元存放的起始位置
int mu, nu, tu;
}RLSMatrix;
3.十字链表
//结点结构
typedef struc OLNode{
int row, col;//非零元所在行、列
int val;//非零元的值
struct OLNode *right, *down;//同行、同列的下一个非零元
}OLNode, *OLink;
//十字链表结构
typedef struct{
OLink rhead[M], chead[N];//行、列指针数组
int mu, nu, tu;//行、列数、非零元数
}CrossList;