第五章1数组.docx

上传人:b****5 文档编号:12189697 上传时间:2023-04-17 格式:DOCX 页数:13 大小:39.36KB
下载 相关 举报
第五章1数组.docx_第1页
第1页 / 共13页
第五章1数组.docx_第2页
第2页 / 共13页
第五章1数组.docx_第3页
第3页 / 共13页
第五章1数组.docx_第4页
第4页 / 共13页
第五章1数组.docx_第5页
第5页 / 共13页
点击查看更多>>
下载资源
资源描述

第五章1数组.docx

《第五章1数组.docx》由会员分享,可在线阅读,更多相关《第五章1数组.docx(13页珍藏版)》请在冰豆网上搜索。

第五章1数组.docx

第五章1数组

第五章数组

5.1数组的类型定义

ADTArray{

数据对象:

D={aj1,j2,...,ji,...jn|ji=0,...,bi-1,i=1,2,..,n,

n(>0)称为数组的维数,

bi是数组第i维的长度,

ji是数组元素的第i维下标,

aj,...,j∈ElemSet}

数据关系:

R={R1,R2,...,Rn}

Ri={|0jkbk-1,

1kn且ki,0jibi-2,

aj1,...,ji,...,jn,aj1,...ji+1,...,jn∈D,i=2,...,n}

基本操作:

InitArray(&A,n,bound1,...,boundn)

操作结果:

若维数n和各维长度合法,则构造相应的

数组A,并返回OK。

DestroyArray(&A)

操作结果:

销毁数组A。

Value(A,&e,index1,...,indexn)

初始条件:

A是n维数组,e为元素变量,随后是n

个下标值。

操作结果:

若各下标不超界,则e赋值为所指定的A

的元素值,并返回OK。

Assign(&A,e,index1,...,indexn)

初始条件:

A是n维数组,e为元素变量,随后是n

个下标值。

操作结果:

若下标不超界,则将e的值赋给所指定的

A的元素,并返回OK。

}ADTArray

5.2数组的顺序表示和实现

类型特点:

1)只有引用型操作,没有加工型操作;

2)数组是多维的结构,而存储空间是一个一维的结构。

有两种顺序映象的方式:

1)以行序为主序(低下标优先);

2)以列序为主序(高下标优先);

在C++语言中,设有数组类,例如:

template

classArray

{

private:

Elem*alist;

intsize;

voidError(ErrorTypeerror,intbadIndex=0)const;

public:

Array(intsz=50);

Array(constArray&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

{

private:

inti,j;//该非零元的行下标和列下标

doublee;

public:

};

classTSMatrix

{

private:

Arraydata;//非零元的三元组表,

intmu,nu,tu;

//矩阵的行数、列数和非零元个数

public:

TSMatrix(intmz,intnz,inttz,intsz);

TSMatrix(constTSMatrix&M);

~TSMatrix(void);

TSMatrix&Transpose(constTSMatrix&M);

TSMatrix&Sum(constTSMatrix&M,

constTSMatrix&N);

TSMatrix&Product(constTSMatrix&M,

constTSMatrix&N);

};

 

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<=M.nu;++col)

cpot[col]=cpot[col-1]+num[col-1];

//求M中每一列的第一个非零元在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];

}//for

}//if

return*T;

}//Transpose

这种三元组顺序表又称有序的双下标表示

其它表示形式:

1)伪地址法

k=in+j+1

2)有序的二元组表

二、行逻辑联接的顺序表

重新定义稀疏矩阵类如下:

classRLSMatrix{

private:

Arraydata;//非零元三元组表,

intmu,nu,tu;

//矩阵的行数、列数和非零元个数

Arrayrpos;

//矩阵中每一行的第一个非零元

//在三元组表中的位序

修改前述的稀疏矩阵的类定义,增加一个数据成员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&M,

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;arow<=M.mu;++arow){

//处理M的每一行

ctemp[]=0;//当前行各元素累加器清零

Q.rpos[arow]=Q.tu+1;

//设置当前行第一个非零元在三元组表中的位序

for(p=M.rpos[arow];p

//对当前行中每一个非零元

brow=M.data[p].j;//找到对应元在N中的行号

if(brow

else{t=N.tu+1}

//t指示该行中最后一个非零元的位置

for(q=N.rpos[brow];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]};

}//if

}//forarow

}//if

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.049

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->j==pb->j且pa->e+pb->e==0,则需要在A矩阵的链表中删除pa所指的结点。

此时,需改变同一行中前一结点的right域值,以及同一列中前一结点的down域值。

为了便于插入和删除结点,还需要设立一些辅助指针。

其一是,在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;j<=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;p->down=NULL;}

else{

p->down=hl[p->j]->down;hl[p->j]->down=p;}

hl[p->j]=p;

②若pa->j〈pb->j且pa->j!

=0,则令pa指向本行下一个非零元结点,即

pre=pa;pa=pa->right;

③若pa->j==pb->j,则将B中当前结点的值加到A中当前结点上,即

pa->e+=pb->e;

此时若pa->e!

=0,则指针不变,否则删除A中该结点,即行表中指针变为

ifpre==NULLrhead[pa->i]=pa->right;

else{pre->right=pa->right;}

p=pa;pa=pa->right;

同时,为了改变列表中的指针,需要先找到同一列中的前驱结点,且让hl[pa->j]指

向该结点,然后如下修改相应指针:

ifchead[p->j]==p

chead[p->j]=hl[p->j]=p->down;

else{hl[p->j]->down=p->down;}

free(p);

(3)若本行不是最后一行,则令pa和pb指向下一行的第一个非零元结点,转

(2);否则结束。

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 表格模板 > 合同协议

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1