第五章1数组Word文档格式.docx
- 文档编号:4512683
- 上传时间:2023-05-03
- 格式:DOCX
- 页数:13
- 大小:39.36KB
第五章1数组Word文档格式.docx
《第五章1数组Word文档格式.docx》由会员分享,可在线阅读,更多相关《第五章1数组Word文档格式.docx(13页珍藏版)》请在冰点文库上搜索。
1)只有引用型操作,没有加工型操作;
2)数组是多维的结构,而存储空间是一个一维的结构。
有两种顺序映象的方式:
1)以行序为主序(低下标优先);
2)以列序为主序(高下标优先);
在C++语言中,设有数组类,例如:
template<
classElem>
classArray
{
private:
Elem*alist;
intsize;
voidError(ErrorTypeerror,intbadIndex=0)const;
public:
Array(intsz=50);
Array(constArray<
Elem>
&
A);
~Array(void);
Array<
oprator=(constArray<
rhs);
Elem&
oprator[](inti);
opratorElem*(void)const
intListSize(void)const;
voidResize(intsz);
}
5.3稀疏矩阵的压缩存储
假设m行n列的矩阵含t个非零元素,则称
为稀疏因子
通常认为0.05的矩阵为稀疏矩阵
若以常规方法,即以二维数组表示高阶的稀疏矩阵,则:
1)零值元素占的空间很大;
2)计算中进行了很多和零值的运算;
解决问题的原则:
1)尽可能少存或不存零值元素;
2)尽可能减少没有实际意义的运算;
3)运算方便;
即:
能尽可能快地找到与下标值(i,j)对应的非零值元;
能尽可能快地找到同一行或同一列的非零值元;
通常,有两类稀疏矩阵:
1)特殊矩阵
例如:
三角矩阵
对角矩阵
它们的压缩存储处理相对地比较简单
2)随机稀疏矩阵
随机矩阵中的非零元分布不规则
下面讨论(随机)稀疏矩阵的几种压缩存储处理方法
一、三元组顺序表
classTriple
{
inti,j;
//该非零元的行下标和列下标
doublee;
…
};
classTSMatrix
Array<
Triple>
data;
//非零元的三元组表,
intmu,nu,tu;
//矩阵的行数、列数和非零元个数
TSMatrix(intmz,intnz,inttz,intsz);
TSMatrix(constTSMatrix&
M);
~TSMatrix(void);
TSMatrix&
Transpose(constTSMatrix&
Sum(constTSMatrix&
M,
constTSMatrix&
N);
Product(constTSMatrix&
constTSMatrix&
…
TSMatrix&
TSMatrix:
:
Transpose(constTSMatrix&
M){
//采用三元组顺序表存储表示,求稀疏矩阵M的
//转置矩阵
TSMatrixT(M.nu,M.mu,M.tu,M.data.ListSize());
if(T.tu){
for(col=1;
col<
=M.nu;
++col)num[col]=0;
for(t=1;
t<
=M.tu;
++t)++num[M.data[t].j];
//求M中每一列所含非零元的个数
cpot[1]=1;
for(col=2;
++col)
cpot[col]=cpot[col-1]+num[col-1];
//求M中每一列的第一个非零元在b.data中的序号
for(p=1;
p<
++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];
}//for
}//if
return*T;
}//Transpose
这种三元组顺序表又称有序的双下标表示
其它表示形式:
1)伪地址法
k=in+j+1
2)有序的二元组表
二、行逻辑联接的顺序表
重新定义稀疏矩阵类如下:
classRLSMatrix{
private:
//非零元三元组表,
//矩阵的行数、列数和非零元个数
int>
rpos;
//矩阵中每一行的第一个非零元
//在三元组表中的位序
…
修改前述的稀疏矩阵的类定义,增加一个数据成员rpos,其值在稀疏矩阵的构造函数中确定。
矩阵乘法的精典算法:
for(i=1;
i<
=m1;
++i)
for(j=1;
j<
=n2;
++j){
Q[i][j]=0;
for(k=1;
k<
=n1;
++k)Q[i][j]+=M[i][k]*N[k][j];
}
其时间复杂度为:
O(m1×
n1×
n2)
以行逻辑联接的顺序表表示稀疏矩阵时,
两个稀疏矩阵相乘(QMN)的过程可大致描述如下:
Q初始化;
ifQ是非零矩阵{//逐行求积
for(arow=1;
arow<
=M.mu;
++arow){
//处理M的每一行
ctemp[]=0;
//累加器清零
计算Q中第arow行的积并存入ctemp[]中;
将ctemp[]中非零元压缩存储到Q.data中;
}//forarow
}//if
RLSMatrix&
RLSMarix:
Product(constRLSMatrix&
constRLSMatrix&
N){
//求矩阵乘积Q=MN,采用行逻辑链接存储表示。
if(M.nu!
=N.mu)returnERROR;
sz=M.data.ListSize();
RLSMatrixQ(M.mu,N.nu,0,sz);
//Q初始化
if(M.tu*N.tu!
=0){//Q是非零矩阵
for(arow=1;
//当前行各元素累加器清零
Q.rpos[arow]=Q.tu+1;
//设置当前行第一个非零元在三元组表中的位序
for(p=M.rpos[arow];
M.rpos[arow+1];
++p)
//对当前行中每一个非零元
brow=M.data[p].j;
//找到对应元在N中的行号
if(brow<
N.nu)t=N.rpos[brow+1];
else{t=N.tu+1}
//t指示该行中最后一个非零元的位置
for(q=N.rpos[brow];
q<
t;
++q){
ccol=N.data[q].j;
//乘积元素在Q中列号
ctemp[ccol]+=M.data[p].e*N.data[q].e;
}//forq
}//求得Q中第crow(=arow)行的非零元
for(ccol=1;
ccol<
=Q.nu;
++ccol)//压缩存储该行非零元
if(ctemp[ccol]){//该列元素为非零元
if(++Q.tu>
Q.data.ListSize())
Q.data.Resize(Q.data.ListSize+Q.nu);
Q.data[Q.tu]={arow,ccol,ctemp[ccol]};
return*Q;
}//Product
分析上述算法的时间复杂度有如下结果:
累加器ctemp初始化的时间复杂度为(M.muN.mu),求Q的所有非零元的时间复杂度为(M.tuN.tu/N.mu),进行压缩存储的时间复杂度为(M.muN.nu),
总的时间复杂度就是(M.muN.nu+M.tuN.tu/N.mu)。
若M是m行n列的稀疏矩阵,N是n行p列的稀疏矩阵,
则M中非零元的个数M.tu=Mmn,
N中非零元的个数N.tu=Nnp,
此时算法5.3的时间复杂度就是(mp(1+nMN)),当M<
0.05和N<
0.05及n<
1000时,算法5.3的时间复杂度就相当于(mp),显然,这是一个相当理想的结果。
如果事先能估算出所求乘积矩阵Q不再是稀疏矩阵,则以二维数组表示Q,相乘的算法也就更简单了。
从乘积的计算公式来看:
如果存在k,使M[i][k]N[k][j]0,则Q[i][j]=0的可能性也很小。
假设这种可能性为零,且
m=n=p,m=n=
则Q的稀疏因子
n
5
10
20
40
80
120
160
0.05
0.0124
0.025
0.049
0.095
0.181
0.260
0.330
0.10
0.096
0.192
0.331
0.552
0.700
0.800
0.20
0.185
0.335
0.558
0.805
0.962
0.993
0.999
可见,对n的变化有个临界点n(),
当n>
n()时,
三、十字链表
当矩阵的非零元个数和位置在操作过程中变化较大时,就不宜采用顺序存储结构来表示三元组的线性表。
例如,在作“将矩阵B加到矩阵A上”的操作时,由于非零元的插入或删除将会引起A.data中元素的移动。
为此,对这种类型的矩阵,采用链式存储结构表示三元组的线性表更为恰当。
在链表中,每个非零元可用一个含五个域的结点表示,其中i,j和e三个域分别表示该非零元所在的行、列和非零元的值,向右域right用以链接同一行中下一个非零元,向下域down用以链接同一列中下一个非零元。
同一行的非零元通过right域链接成一个线性链表,同一列的非零元通过down域链接成一个线性链表,每个非零元既是某个行链表中的一个结点,又是某个列链表中的一个结点,整个矩阵构成了一个十字交叉的链表,故称这样的存储结构为十字链表,可用两个分别存储行链表的头指针和列链表的头指针的一维数组表示之。
例如:
矩阵M的十字链表如下图所示。
113145
22–1
312
假设非空指针pa和pb分别指向矩阵A和B中行值相同的两个结点,pa==NULL表明矩阵A在该行中没有非零元,则上述四种情况的处理过程为:
(1)若pa==NULL或pa->
j〉pb->
j,则需要在A矩阵的链表中插入一个值为bi,j的结点。
此时,需改变同一行中前一结点的right域值,以及同一列中前一结点的down域值。
(2)若pa->
j〈pb->
j,则只要将pa指针往右推进一步。
(3)若pa->
j==pb->
j且pa->
e+pb->
e!
=0,则只要将ai,j+bi,j的值送到pa所指结点的e域即可,其它所有域的值都不变。
(4)若pa->
e==0,则需要在A矩阵的链表中删除pa所指的结点。
为了便于插入和删除结点,还需要设立一些辅助指针。
其一是,在A的行链表上设pre指针,指示pa所指结点的前驱结点;
其二是,在A的每一列的链表上设一个指针hl[j],它的初值和列链表的头指针相同,
即hl[j]=chead[j]。
算法:
(1)初始,令pa和pb分别指向A和B的第一行的第一个非零元素的结点,即
pa=A.rhead[1];
pb=B.rhead[1];
pre=NULL;
且令hl初始化for(j=1;
=A.nu;
++j)hl[j]=A.chead[j];
(2)重复本步骤,依次处理本行结点,直到B的本行中无非零元素的结点,即pb==NULL为止:
①若pa==NULL或pa->
j〉pb->
j(即A的这一行中非零元素已处理完),则
需在A中插入一个pb所指结点的复制结点。
假设新结点的地址为p,则A的行表
中的指针作如下变化:
ifpre==NULLrhead[p->
i]=p;
else{pre->
right=p;
p->
right=pa;
pre=p;
A的列链表中的指针也要作相应的改变。
首先需从hl[p->
j]开始找到新结点在同一列
中的前驱结点,并让hl[p->
j]指向它,然后在列链表中插入新结点:
ifchead[p->
j]==NULL
{chead[p->
j]=p;
down=NULL;
else{
down=hl[p->
j]->
down;
hl[p->
down=p;
②若pa->
j〈pb->
j!
=0,则令pa指向本行下一个非零元结点,即
pre=pa;
pa=pa->
right;
③若pa->
j,则将B中当前结点的值加到A中当前结点上,即
pa->
e+=pb->
e;
此时若pa->
e!
=0,则指针不变,否则删除A中该结点,即行表中指针变为
ifpre==NULLrhead[pa->
i]=pa->
right=pa->
p=pa;
pa=pa->
同时,为了改变列表中的指针,需要先找到同一列中的前驱结点,且让hl[pa->
j]指
向该结点,然后如下修改相应指针:
ifchead[p->
j]==p
chead[p->
j]=hl[p->
j]=p->
else{hl[p->
down=p->
free(p);
(3)若本行不是最后一行,则令pa和pb指向下一行的第一个非零元结点,转
(2);
否则结束。
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 第五 数组