矩阵的压缩存储
矩阵压缩:
指为多值相同的元素只分配一个存储空间
对零元素不分配空间
特殊矩阵的压缩存储
- 对称矩阵
定义:
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;
}