特殊矩阵的压缩存储(对称矩阵,三角矩阵,对角矩阵,稀疏矩阵的顺序,链序存储,十字链表的建立)

特殊矩阵的压缩存储

压缩存储的定义:
若多个数据元素的值都相同,则只分配一个元素值的存储空间,且
零元素不占存储空间。
能够压缩的一些矩阵: 一些特殊矩阵,如:对称矩阵,对角矩阵,三角矩阵,稀疏矩阵等。 稀疏矩阵定义:
矩阵中非零元素的个数较少(一 般小于5%)

一、对称矩阵

特点:

在n×n的矩阵a中,aij=aji(1<=i,j<=n)

存储方法:

只存储下(或者上)三角(包括主对角线)的数据元素。共占用n(n+1)/2个元素空间 可以以行序为主序将元素存放在一个一维数组**sa[n(n+1)/2]**中。

二、三角矩阵

特点:

对角线以下(或者以上)的数据元素(不包括对角线)全部为常数c

存储方法:

重复元素c共享一个元素存储空间,共占用m(n+1)/2+1个元素 空间:sa[1…n(n+1)/2+1] [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3ZUT9pwg-1666678796589)(C:\Users\Lenovo\AppData\Roaming\Typora\typora-user-images\image-20220422103720542.png)]

三、对角矩阵(带状矩阵)

特点:

在n×n的方阵中,所有非零元素都集中在以主对角线为中心的带状区域中,区域外的值全为0,则成为对角矩阵。常见的有三对角矩阵,五对角矩阵,七对角矩阵等。

例:三对角矩阵

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Fzo72aQN-1666678796590)(C:\Users\Lenovo\AppData\Roaming\Typora\typora-user-images\image-20220422104535295.png)]

a[k]=2+3(i-2)+(j-i-2)-1=2*i+j-3

存储方法

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-t72WAtgH-1666678796591)(C:\Users\Lenovo\AppData\Roaming\Typora\typora-user-images\image-20220422104707703.png)]

四、稀疏矩阵的存储

**稀疏矩阵:**设在m×n的矩阵中有t个非零元素。令x=t/(m×n),当x<=0.05时称为稀疏矩阵

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PA257udr-1666678796591)(C:\Users\Lenovo\AppData\Roaming\Typora\typora-user-images\image-20220422105518826.png)]

**三元组(i,j,aij)**唯一确定矩阵的一个非零元

**压缩存储原则:**存各非零元的值,行列位置和矩阵的行列数

稀疏矩阵的压缩存储方法——顺序存储结构

三元组顺序表

**[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CA7fiESW-1666678796592)(C:\Users\Lenovo\AppData\Roaming\Typora\typora-user-images\image-20220422105954618.png)]**

三元组顺序表又称有序的双下标法

三元组顺序表的优点:非零元在表中按行序有序存储,因此便于进行依行顺序处理的矩阵运算

三元组顺序表的缺点:**不能随机存取。**若按行号存取某一行中的非零元,则需重头开始进行查找。

顺序存储结构
typedef int ElemType;
//----------稀疏矩阵的三元组顺序表存储表示----------
#define MAXSIZE 12500	//假设非零元个数的最大值为12500
typedef struct {
	int i, j;	//该非零元的行下标和列下标
	ElemType e;
} Triple;

typedef struct {
	Triple data[MAXSIZE + 1];		//非零元三元组表,data[0]未用
	int mu, nu, tu;	//矩阵的行数,列数,和非零元的个数
} TSMatrix;
打印矩阵
void PrintfTSMatrix(TSMatrix X){
	//打印矩阵
	int m,n;
	m=X.mu;	//稀疏矩阵的行数
	n=X.nu;	//稀疏矩阵的列数
	int k=1;	//三元组数组的下标
	for(int i=0;i<m;i++){
		for(int j=0;j<n;j++){	//双层循环遍历稀疏矩阵
			if(i==X.data[k].i&&j==X.data[k].j){	//如果i和j能够匹配三元组的下标
				printf("%d\t",X.data[k].e);	//如果能够配对则输出三元组的非零元素的值
				k++;	//继续遍历三元组的元素
			}else{
				printf("0\t");	//如果没有匹配则代表该处位置是0
			}
		}
		printf("\n");
	}
}
求稀疏矩阵的转置矩阵
int TransposeSMatrix(TSMatrix M, TSMatrix &T) {
	//采用三元组表存储表示,求稀疏矩阵M的转置矩阵T
	T.mu = M.nu;
	T.nu = M.mu;
	T.tu = M.tu;
	int q,p;
	if (T.tu) {
		q = 1;
		for (int col = 1; col <= M.nu; ++col) {	//按M的列查找
			for (p = 1; p <= M.tu; ++p) {
				if (M.data[p].j == 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;
					++q;
				}
			}
		}
	}
	return 0;
}
求稀疏矩阵的转置矩阵(快速转置)

链接: 看这个好理解

//快速转置
int FastTransposeSMatrix(TSMatrix M, TSMatrix &T) {
	//采用三元组顺序表存储表示,求稀疏矩阵M的转置矩阵T
	T.mu = M.nu;
	T.nu = M.mu;
	T.tu = M.tu;
	int col, num[1000], cpot[1000];
	int p, q;
	if (T.tu) {
		for (col = 1; col <= M.nu; ++col)	num[col] = 0;
		for (int t = 1; t <= M.tu; ++t)	++num[M.data[t].j];	//求M中每一列含非零元个数
		cpot[1] = 1;	//Cpot[1]=1的用处是第一列的在新三元表T的第一个插入位置为1。Cpot[0]是留给储存三元表行列数和非零元个数的。
		for (col = 2; col <= M.tu; ++col)	cpot[col] = cpot[col - 1] + num[col - 1];	//求第col列中第一个非零元在b.data中的序号
		for (p = 1; p <= M.tu; ++p) {
			col = M.data[p].j;
			q = cpot[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;
			++cpot[col];
		}
	}
	return 0;
}

稀疏矩阵的链式存储结构:十字链表

优点:它能够灵活地插入因运算而产生的新的非零元素,删除因运算而产生的新的零元素,实现矩阵的各种运算。

在十字链表中,矩阵的每一个非零元素用一个结点表示,该结点除了(row,col,value)以外,还要有两个域: **right:**用于链接同一行中的下一个非零元素; **down:**用于链接同一列中的下一个非零元素;

十字链表中结点的结构示意图 : [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7mqx3HyN-1666678796593)(C:\Users\Lenovo\AppData\Roaming\Typora\typora-user-images\image-20220422111324864.png)]

十字链表的建立
CrossList CreatSMatrix_OL(CrossList &M) {
	//创建稀疏矩阵M.采用十字链表存储表示
	int m, n, t;
	int i, j, e;
	OLink p, q;
	scanf("%d%d%d", &m, &n, &t);	//输入M的行数,列数和非零元个数
	M.mu = m;
	M.nu = n;
	M.tu = t;
	if (!(M.rhead = (OLink *)malloc((m + 1) * sizeof(OLink))))	exit(0);
	if (!(M.chead = (OLink *)malloc((n + 1) * sizeof(OLink))))	exit(0);
	for (i = 1; i <= m; i++)	M.rhead[i] = NULL;
	for (j = 1; j <= n; j++)   M.chead[j] = NULL;	//初始化行列头指针向量;各行列链表为空链表
	for (scanf("%d%d%d", &i, &j, &e);i!=0;scanf("%d%d%d",&i,&j,&e)) {
		if(!(p=(OLNode*)malloc(sizeof(OLNode)))){
			printf("初始化三元组失败"); exit(0);
		}
		p->i=i; p->j=j; p->e=e;	//生成结点
		if(NULL==M.rhead[i]||M.rhead[i]->j>j){	
			p->right=M.rhead[i];
			M.rhead[i]=p;
		}
		else{	//寻查在行表中的插入位置
			for(q=M.rhead[i];(q->down)&&q->right->j<j;q=q->right);
			p->right=q->right;	q->right=p;	//完成行插入	
			}
		if(NULL == M.chead[j] || M.chead[j]->i > i){
			p->down=M.chead[j];
			M.chead[j]=p;
		}
		else{	//寻查在列表中的插入位置
			for(q=M.chead[i];(q->down)&&q->down->i<i;q=q->down);
			p->down=q->down;
			q->down=p;
		}	
	}
	return M;
}
十字链表的完整代码

测试样例:

输入矩阵的行数、列数和非0元素个数:3 3 3
2 2 3
2 3 4
3 2 5
0 0 0
输出矩阵M:
2 2 3
3 2 5
2 3 4

#include<stdio.h>
#include<stdlib.h>
typedef struct OLNode {
	int i, j, e; //矩阵三元组i代表行 j代表列 e代表当前位置的数据
	struct OLNode *right, *down; //指针域 右指针 下指针
} OLNode, *OLink;
typedef struct {
	OLink *rhead, *chead; //行和列链表头指针
	int mu, nu, tu;  //矩阵的行数,列数和非零元的个数
} CrossList;
CrossList CreateMatrix_OL(CrossList M);
void display(CrossList M);
int main() {
	CrossList M;
	M.rhead = NULL;
	M.chead = NULL;
	M = CreateMatrix_OL(M);
	printf("输出矩阵M:\n");
	display(M);
	return 0;
}
CrossList CreateMatrix_OL(CrossList M) {
	int m, n, t;
	int i, j, e;
	OLNode *p, *q;
	printf("输入矩阵的行数、列数和非0元素个数:");
	scanf("%d%d%d", &m, &n, &t);
	M.mu = m;
	M.nu = n;
	M.tu = t;
	if (!(M.rhead = (OLink*)malloc((m + 1) * sizeof(OLink))) || !(M.chead = (OLink*)malloc((n + 1) * sizeof(OLink)))) {
		printf("初始化矩阵失败");
		exit(0);
	}
	for (i = 1; i <= m; i++) {
		M.rhead[i] = NULL;
	}
	for (j = 1; j <= n; j++) {
		M.chead[j] = NULL;
	}
	for (scanf("%d%d%d", &i, &j, &e); 0 != i; scanf("%d%d%d", &i, &j, &e)) {
		if (!(p = (OLNode*)malloc(sizeof(OLNode)))) {
			printf("初始化三元组失败");
			exit(0);
		}
		p->i = i;
		p->j = j;
		p->e = e;
		//链接到行的指定位置
		if (NULL == M.rhead[i] || 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);
			p->right = q->right;
			q->right = p;
		}
		//链接到列的指定位置
		if (NULL == M.chead[j] || 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);
			p->down = q->down;
			q->down = p;
		}
	}
	return M;
}
void display(CrossList M) {
	for (int i = 1; i <= M.nu; i++) {
		if (NULL != M.chead[i]) {
			OLink p = M.chead[i];
			while (NULL != p) {
				printf("%d\t%d\t%d\n", p->i, p->j, p->e);
				p = p->down;
			}
		}
	}
}
  • 5
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值