数据结构笔记-矩阵的压缩存储

65 阅读6分钟

一个宝藏视频【数据结构】特殊矩阵的压缩存储/对称矩阵/三角矩阵/对角矩阵(含经典题讲解)_哔哩哔哩_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)

2ec462c2eab0c446cfe4c6c53503895.png

(二)稀疏矩阵(零元多,分布无规则)

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.十字链表

e5c4d279f0b3fb946c2f0b57a28181b.jpg

//结点结构 
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;