矩阵的压缩存储
矩阵压缩:
指为多值相同的元素只分配一个存储空间
对零元素不分配空间
特殊矩阵的压缩存储
-
对称矩阵
-
定义:
A[i][j] = A[j][i]
开辟空间大小:n * n 个元素压缩至n*(n-1)/2个元素
-
使用一维数组
s[k]
以行来存储对称矩阵A(i,j)
的下三角元素,则 -
存储与实现:
#include <stdio.h> #include <stdlib.h> #include <malloc.h> #include <string.h> /**对称矩阵压缩存储**/ void PrintMatrix(double *s,int n); /**存储对称矩阵**/ void storeMatrix(int n){ double *s = new double[n]; int len = n*(n+1)/2; printf("Input the UP_Matrix's element by the order of row\n"); for(int i = 0;i<len;i++){ scanf("%d",&s[i]); } PrintMatrix(s,n); } /**打印矩阵**/ void PrintMatrix(double *s,int n){ int k = 0; for(int i =0;i<n;i++){ for(int j = 0;j<n;j++){ //以行为主存储下三角矩阵 if(i>=j) k = i*(i+1)/2+j; else k = j*(j+1)/2+i; printf("%d\t",s[k]); } printf("\n"); } } void main(){ int n = 0 ; printf("Input the order of the matrix:"); cin >> n; storeMatrix(n); system("pause"); }
-
-
三角矩阵
- 分为上三角矩阵,下三角矩阵
- 与对称矩阵差别不大,仅在上三角矩阵中 矩阵的存储相当于对称矩阵的以列为主的压缩存储
k = i*(2n-i+1)/2 + i
-
对角矩阵
-
所有非零元素集中在主对角线两侧的带状区域内。m对角矩阵是指非零元素在每行中有m个。
-
-
特点:
- 第一行和最后一行有两个非零元素,第2~n-1行有3个非零元素
- 使用一维数组存储,需要存储
3*n-2
个元素 - 一维数组
s[k]
和A[i][j]
的对应关系为:k = 2*i+j
稀疏矩阵的压缩存储
稀疏矩阵:非零元较零元少,且分布没有一定规律的矩阵
压缩存储原则:只存矩阵行列维数,和非零元的行列下标及值
-
三元组表
-
即用顺序存储结构表示三元组表,记录非零元素的行和列号
-
类型定义【行优先存储】:
-
#define MAXSIZE 100 Typedef struct{ int i,j; ElemType e; }Tripe; Typedef struct{ Tripe data[MAXSIZE+1]; int mu,nu,tu; }TSMatrix;
-
-
转置的实现:
-
方法一、常规思路
- 将矩阵的行、列维数互换
- 将每个三元组中的i 和 j 相互调换
- 重排三元组次序
bool TransposeSMatrix(TSMatrix M, TSMatrix T) { T.mu = M.nu;T.nu = M.mu; T.tu = M.tu; if(M.tu) { q = 1; for(int col = 1; col <= M.mu; col++) { for(int 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++; } } } }else return false; return true; }
其实,效率并不高,基本操作是将M.data中的元素转到T.data中,利用两个循环完成,多次遍历M.data。
所以,我们就想,能不能在只扫描一次的基础上,完成M到T的转置?
-
方法二、快速转置算法
-
M中非零元以行为主序存储,故各列的分零元的存储位置不是“连续”,但是,存储顺序却是一致的。
-
思路:
- 求M中各列非零元个数num[ ];
- 求M中各列第一个非零元在T.data中的下标cpos[ ];
- 对M.data进行一次扫描, 遇到col列的第一个非零元三元组时,按cpos[col ]的位置,将其放至T.data中,当再次遇到col列的非零元三元组时,只须顺序放到col列元素的后面
-
bool FastTransposeSMatrix(TSMatrix M, TSMatrix T) { T.mu=M.nu; T.nu=M.mu; T.tu=M.tu; if(M.tu) { for (col=1; col<=M.nu; ++col) num[col]=0; for (t=1; t<=M.tu; ++t) ++num[M.data[t].j]; //求M中每一列非零元个数 //求第 col列中第一个非零元在T.data中的序号 cpot[1]=1; for(col=2; col<=M.tu; ++col) cpot[col]=cpot[col-1]+num[col-1]; 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]; } } }//只有在tu非常小的时候才有意义
-
-
-
-
十字链表
-
当矩阵的非零元个数和位置在操作过程中变化较大时,采用链式存储的方式,设行指针数组和列指针数组,分别指向行、列第一个非零元,构成十字链表。
-
-
类型定义:
typedef struct OLNode{ int i,j; ElemType e; struct OLNode *right,*down; }OLNode,*OList; typedef struct{ OLink *Rhead,*Chead; int mu,nu,tu; }CrossList;
-
建立稀疏矩阵并用十字链表存储
bool CreateSMatrix(CrossList M) { OLNode *p,*q; cin >> m >> n >> t;//读入行、列及非零元个数 M->mu = m;M->nu = n;M->tu = t; //声明指针数组,并初始化为NULL M->Rhead = (OLink*)malloc(m*sizeof(OLink)); M->Chead = (OLink*)malloc(n*sizeof(OLink)); for(int k = 0; k < m; k++) M->Rhead[k] = NULL; for(int k = 0; k < m; k++) M->Chead[k] = NULL; /*下面进行结点的输入 操作为 拿到一个结点,分别插入 行链数组相应的行链表,及列链数组相应的列链表,插入两种情况【以行链表为例】,一种为行链表为空,或是要插入结点列大小小于链表已有结点的列大小,插在前面;另一种为 列数较大,需插在后面,一个循环去搜应该插到哪里 */ for(int index = 0; index < t; index++) { cin >> i >> j >> e; p = (OLink)malloc(sizeof(OLNode)); p->i = i; p->j = j; p->e = e; 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) ; 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) ; p->down = q->down; q->down = p; } } return true; }
-