1.稀疏矩阵:矩阵中绝大多数的元素值为零,只有少数的非零元素值;
2.对稀疏矩阵采用压缩存储的目的是为了节省存储空间,并且,稀疏矩阵压缩存储后,还要能够比较方便地访问其中的没个元素(包括零元素和非零元素);
3.对稀疏矩阵进行压缩存储有两种方法:稀疏矩阵的三列二维数组表示和十字链表方法。
稀疏矩阵的三列二维数组表示:
(1)每个非零元素用三元组表示:(i,j,v)分别对应(行,列,非零元素);
(2) 为了表示唯一性,除了每个非零元素用一个三元组表示外,在所有的非零元素三元组之前添加一组信息(I,J,V)对应(总行数,总列数,非零元素个数),也就是说,我们要确定确切的稀疏矩阵,必须先知道该稀疏矩阵的总行数,总列数,非零个数;
(3)为了便于在三列二维数组B中访问稀疏矩阵A中的各元素,通常还附设两个长度与稀疏矩阵A的行数相同的向量POS与NUM。其中POS[K]表示稀疏矩阵A中的第k行的第一个非零元素(如果有的话)在三列二维组B中的行号;NUM[k]表示稀疏矩阵A中第k行中非零元素的个数;
pos[0]=0; pos[k]=pos[k-1]+num[k-1]
4.三列二维数组表示的稀疏矩阵类如下:
文件名:X_Array.h
#include <iostream>
#include <iomanip>
using namespace std;
template <class T>
struct B
{
int i; //非零元素所在行号
int j; //非零元素所在列号
T v; //非零元素值
};
//三列二维数组表示的稀疏矩阵类
template <class T>
class X_Array
{
private:
int mm; //稀疏矩阵行数
int nn; //稀疏矩阵列数
int tt; //非零个数
B<T> *bb; //三列二维数组空间
int *pos; //某行第一个非零元素在b中的下标
int *num; //某行非零元素的个数
public:
void in_X_Array(); // 以三元组形式键盘输入稀
void cp_X_Array(int,int,int,B<T>[]); // 复制三元数组
void th_X_Array(int,int,T[]); // 由一般稀疏矩阵转换
void prt_X_Array(); // 按行输出稀疏矩阵
X_Array tran_X_Array(); // 稀疏矩阵转置
X_Array operator +(X_Array &); // 稀疏矩阵相加
X_Array operator *(X_Array &); // 稀疏矩阵相乘
};
// 以三元组形式键盘输入稀疏矩阵非零元素
template <class T>
void X_Array<T>::in_X_Array()
{
int k,m,n;
cout<<"输入行数 列数 非零元素个数:"<<endl;
cin>>mm>>nn>>tt;
bb=new B<T>[tt];
cout<<"输入行号 列号 非零元素值:"<<endl;
for(k=0;k<tt;k++)
{
cin>>m>>n>>bb[k].v;
bb[k].i=m-1;
bb[k].j=n-1;
}
pos=new int[mm];
num=new int[mm];
for(k=0;k<mm;k++) //num向量初始化
num[k]=0; //初始化mm行的每行num都设为零
for(k=0;k<tt;k++)
num[bb[k].i]=num[bb[k].i]+1; //提取每个非零元素的行号,出现行号的数组num值加1,当n个非零元素在同一行时,则加n次。
pos[0]=0;
for(k=1;k<mm;k++) //构造pos向量
pos[k]=pos[k-1]+num[k-1]; //pos[k]表示在稀疏矩阵第k行中第一个非零元素在数组B中的行号
return;
}
//复制三元数组
template <class T>
void X_Array<T>::cp_X_Array(int m,int n,int t,B<T> b[])
{
int k;
mm=m;
nn=n;
tt=t;
bb=new B<T>[tt];
for(k=0;k<tt;k++)
{
bb[k].i=b[k].i-1;
bb[k].j=b[k].j-1;
bb[k].v=b[k].v;
}
pos=new int[mm];
num=new int[mm];
for(k=0;k<mm;k++) //num向量初始化
num[k]=0; //初始化mm行的每行num都设为零
for(k=0;k<tt;k++)
num[bb[k].i]=num[bb[k].i]+1; //提取每个非零元素的行号,出现行号的数组num值加1,当n个非零元素在同一行时,则加n次。
pos[0]=0;
for(k=1;k<mm;k++) //构造pos向量
pos[k]=pos[k-1]+num[k-1]; //pos[k]表示在稀疏矩阵第k行中第一个非零元素在数组B中的行号
return;
}
// 由一般稀疏矩阵转换
template <class T>
void X_Array<T>::th_X_Array(int m,int n,T a[])
{
int k,t=0,p,q;
T d;
for(k=0;k<m*n;k++) //统计非零个数
if(a[k]!=0) t++;
mm=m;nn=n;tt=t;
bb=new B<T>[tt];
k=0;
for(p=0;p<m;p++)
for(q=0;q<n;q++)
{
d=a[p*n+q];
if(d!=0) //非零元素
{
bb[k].i=p;
bb[k].j=q;
bb[k].v=d;
k=k+1;
}
}
pos=new int[mm];
num=new int[mm];
for(k=0;k<mm;k++) //num向量初始化
num[k]=0; //初始化mm行的每行num都设为零
for(k=0;k<tt;k++)
num[bb[k].i]=num[bb[k].i]+1; //提取每个非零元素的行号,出现行号的数组num值加1,当n个非零元素在同一行时,则加n次。
pos[0]=0;
for(k=1;k<mm;k++) //构造pos向量
pos[k]=pos[k-1]+num[k-1]; //pos[k]表示在稀疏矩阵第k行中第一个非零元素在数组B中的行号
return;
}
// 按行输出稀疏矩阵
template <class T>
void X_Array<T>::prt_X_Array()
{
int k,kk,p;
for(k=0;k<mm;k++) //按行输出
{
p=pos[k];
for(kk=0;kk<nn;kk++) //输出一行
if((bb[p].i==k)&&(bb[p].j==kk)) //输出非零元素
{
cout<<setw(8)<<bb[p].v;p=p+1;
}
else
cout<<setw(8)<<0;
cout<<endl;
}
return;
}
// 稀疏矩阵转置
template <class T>
X_Array<T> X_Array<T>::tran_X_Array()
{
X_Array<T> at; //定义转置矩阵对象
int k,p,q;
at.mm=nn;
at.nn=mm;
at.tt=tt;
at.bb=new B<T>[tt];
k=0;
for(p=0;p<nn;p++) //按列扫描所有非零元素
for(q=0;q<tt;q++)
{
if(bb[q].j==p) // 将非零元素一次存放到转置矩阵的三列二维数组中
{
at.bb[k].i=bb[q].j;
at.bb[k].j=bb[q].i;
at.bb[k].v=bb[q].v;
k++;
}
}
at.pos=new int[at.mm];
at.num=new int[at.mm];
for(k=0;k<at.mm;k++)
at.num[k]=0;
for(k=0;k<at.tt;k++)
at.num[at.bb[k].i]=at.num[at.bb[k].i]+1;
at.pos[0]=0;
for(k=1;k<at.mm;k++)
at.pos[k]=at.pos[k-1]+at.num[k-1];
return (at); //返回转置矩阵
}
// 稀疏矩阵相加
template <class T>
X_Array<T> X_Array<T>::operator+ (X_Array<T> &b)
{
X_Array<T> c;
B<T> *a;
T d;
int m,n,k,p;
if((mm!=b.mm)||(nn!=b.nn))
cout<<"不能相加!"<<endl;
else
{
a=new B<T>[tt+b.tt]; //临时申请一个三列二维数组空间
p=0;
for(k=0;k<mm;k++) //逐行处理
{
m=pos[k];n=b.pos[k];
while((bb[m].i==k)&&(b.bb[n].i==k)) //行号相同
{
if(bb[m].j==b.bb[n].j) //列号相同则相加
{
d=bb[m].v+b.bb[n].v;
if(d!=0) //相加后非零
{
a[p].i=k;a[p].j=bb[m].j;
a[p].v=d;p=p+1;
}
m=m+1;n=n+1;
}
else if(bb[m].j<b.bb[n].j) //列号不同则复制列号小的一项
{
a[p].i=k;a[p].j=bb[m].j;
a[p].v=bb[m].v;p=p+1;
m=m+1;
}
else //列号不同复制另一项
{
a[p].i=k;a[p].j=b.bb[m].j;
a[p].v=b.bb[m].v;p=p+1;
n=n+1;
}
}
while(bb[m].i==k) //复制矩阵中本行剩余的非零元素
{
a[p].i=k;a[p].j=bb[m].j;
a[p].v=bb[m].v;p=p+1;
m=m+1;
}
while(b.bb[n].i==k) //复制另一矩阵中本行剩余的非零元素
{
a[p].i=k;a[p].j=b.bb[m].j;
a[p].v=b.bb[m].v;p=p+1;
n=n+1;
}
}
c.mm=mm;c.nn=nn;c.tt=p;
c.bb=new B<T>[p]; //申请一个三列二维数组空间
for(k=0;k<p;k++) //复制临时三列二维数组空间中的元素
{
c.bb[k].i=a[k].i;
c.bb[k].j=a[k].j;
c.bb[k].v=a[k].v;
}
delete a; //释放临时三列二维数组空间
c.pos=new int[c.mm];
c.num=new int[c.mm];
for(k=0;k<c.mm;k++)
c.num[k]=0;
for(k=0;k<c.tt;k++)
c.num[c.bb[k].i]=c.num[c.bb[k].i]+1;
c.pos[0]=0;
for(k=1;k<c.mm;k++)
c.pos[k]=c.pos[k-1]+c.num[k-1];
}
return c;
}
template <class T>
X_Array<T> X_Array<T>::operator* (X_Array<T> &b)
{
X_Array<T> cc;
int k,m,n,p,t;
T *c; //定义乘积矩阵
if(nn!=b.mm)
cout<<"两矩阵无法相乘!"<<endl;
else
{
c=new T[mm*b.nn]; //申请乘积矩阵的临时空间
k=0;
for(m=0;m<mm;m++)
{
for(n=0;n<b.nn;n++)
{
c[k]=0;k=k+1; //乘积矩阵元素初始化为零
}
}
for(m=0;m<tt;m++) //对于左矩阵中的每一个非零元素
{
k=bb[m].j; //左矩阵中非零元素的列值K
n=b.pos[k]; //右矩阵中行号与K相同的第一个非零元素的位置
t=b.pos[k]+b.num[k];//右矩阵中行号与k相同的最后一个非零元素的位置
while(n!=t)
{
p=bb[m].i*b.nn+b.bb[n].j;//在乘积矩阵中的位置
c[p]=c[p]+bb[m].v*b.bb[n].v;//累加非零元素的乘积
n=n+1;
}
}
cc.th_X_Array(mm,b.nn,c); //由一般的稀疏矩阵转换成用三列二维数组表示
delete c; //释放乘积矩阵的临时空间
}
return cc;
}
2.实例应用:
#include "test2.h"
#include<stdlib.h>
int main()
{
B<double> a[8]={{1,3,3.0},{1,8,1.0},{3,1,9.0},{4,5,7.0},{5,7,6.0},{6,4,2.0},{6,6,3.0},{7,3,5.0}};
X_Array<double> x,y,z,xt,c;
x.cp_X_Array(7,8,8,a);
cout<<"输出稀疏矩阵X:"<<endl;
x.prt_X_Array();
xt=x.tran_X_Array();
cout<<"输出稀疏矩阵x的转置xt:"<<endl;
xt.prt_X_Array();
y.in_X_Array();
cout<<"输出稀疏矩阵y:"<<endl;
y.prt_X_Array();
z=x+y;
cout<<"输出稀疏矩阵z=x+y"<<endl;
z.prt_X_Array();
c=x*xt;
cout<<"输出稀疏矩阵c=x*y"<<endl;
c.prt_X_Array();
system("pause");
return 0;
}
3.实验结果: