数据结构第四章.docx

上传人:b****5 文档编号:8172865 上传时间:2023-01-29 格式:DOCX 页数:28 大小:596.73KB
下载 相关 举报
数据结构第四章.docx_第1页
第1页 / 共28页
数据结构第四章.docx_第2页
第2页 / 共28页
数据结构第四章.docx_第3页
第3页 / 共28页
数据结构第四章.docx_第4页
第4页 / 共28页
数据结构第四章.docx_第5页
第5页 / 共28页
点击查看更多>>
下载资源
资源描述

数据结构第四章.docx

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

数据结构第四章.docx

数据结构第四章

第四章数组和串

数组

数组的定义及其基本操作

数组的存贮结构

特殊矩阵的压缩存储

稀疏矩阵的压缩存储

数组的定义及其基本操作

数组的定义:

数组(Array):

是n(n>1)个相同类型的数据元素a0,a1,…,an-1构成的有限序列,且该有限序列存储在块地址连续的内存单元中。

(即物理存储与逻辑结构相一致)

•数组中的数据元素数目固定。

•数组中的每个数据元素具有相同的数据类型。

•数组中的每个数据元素都和一组唯一的下标值相对应。

•数组是一种随机存储结构,可随机存取数组中的任意数据元素。

•地址计算:

一维数组同线性表相同;

计算公式:

Loc(ai)=Loc(a0)+i*k(0<=i

注:

Loc(a0)是a0元素的地址,Loc(ai)是ai元素的地址,k是每个元素所占的存储单元数。

二维数组

设m行n列的二维数组Am*n,有

地址计算:

1、按行序列序计算:

Loc(aij)=Loc(a11)+[(i-1)n+(j-1)]*l

注:

k是每个元素所占的单元数。

2、按列序序列计算:

Loc(aij)=Loc(a11)+[(j-1)m+(i-1)]*l

数组的基本操作

1.arraylength(D)

2.Storage(D,i,x)

3.Get(D,i,x)

数组的存储结构

数组一般在计算机中采用线性结构存储,对于一维数组同线性表相同;关于二维数组和多维数组,一般语言中采用行序列序,即先存第一(或零)行,然后第二行依次存储。

数组也允许有两种存储分配方法:

静态和动态。

•静态数组:

运行期间数组大小不能改变,需预先给出。

•动态数组:

可以动态建立和动态撤消的数组。

动态数组:

一维动态数组

int*a;

a=(int*)malloc(n*sizeof(int));

a[0]…a[n-1]

free(a);

二维动态数组

int**a,i;

a=(int**)malloc(row*sizeof(int*));

for(i=0;i

a[i]=(int*)malloc(col*sizeof(int));

释放空间free(a)?

for(i=0;i

free(a[i]);

free(a);

特殊矩阵的压缩存储

*特殊矩阵

1、对称矩阵的压缩存储

对称矩阵

•定义:

设n阶方阵中的元素关于主对角线对称,即满足条件:

aij=aji1<=i,j<=n,这样的方阵称为n阶对称方阵。

•存储方法:

只存储矩阵中上三角或下三角中的元素,这样可以将n2个元素压缩存储到n(n+1)/2个元素。

即用一维数组a[n*(n+1)/2]来存储该矩阵,一维数组中的元素同二维数组.元素的对应关系:

i(i-1)/2+j-1当i>=j时

k=

j(j-1)/2+i-1当j>i时

则[n(n+1)/2]为n阶对称方阵的压缩存储(下三角存储即用行序列序)。

2、n阶下三角矩阵

3、对角矩阵

Loc(aij)=Loc(a11)+2(i-1)+(j-1)

4、稀疏矩阵的压缩存储

定义:

非零元较零元少,且分布没有一定规律的矩阵

假设在m*n的矩阵中,有t个元素不为零,令,称为矩阵的稀疏因子。

通常认为≤0.05时称为稀疏矩阵。

稀疏矩阵M和T

M由{(1,2,12),(1,3,9),(3,1,-3),(3,6,14),(4,3,24),(5,2,18),(6,1,15),(6,4,-7)}和矩阵维数(6,7)唯一确定

*压缩储存

稀疏矩阵的压缩存储方法是只存非零元素,但由于稀疏矩阵元素中的非零元素是没有规律的,所以在存储时还必须存储每个元素的行下标和列下标值。

这样稀疏矩阵中的每一个非零元素需由一个三元组(i,j,aij)唯一确定。

稀疏矩阵的压缩存储方法:

链式存储结构——三元组十字链表

稀疏矩阵的三元组也可采用链式存储结构。

单链表结构,

缺点:

操作复杂度高——由链表的非随机存取特性限定的。

十字链表

*设行指针数组和列指针数组,分别指向每行、列第一个非零元

*结点定义

typedefstructnode

{introw,col,val;

structnode*down,*right;

}JD;

从键盘接收信息建立十字链表算法

1、初始化头指针向量

2、初始化各行列链表

3、动态生成结点,输入非零元

4、将结点插入行

5、将结点插入列

 

StatusCreateSMatrix_OL(CrossList&M)

{//创建稀疏矩阵M。

采用十字链表存储表示

if(M)free(M);

scanf(&m,&n,&t);//输入M的行数、列数和非零元个数

M.mu=m;M.nu=n;M.tu=t;

if(!

(M.rhead=(Olink*)malloc((m+1)*sizeof(Olink))))

exit(overflow);

if(!

(M.chead=(Olink*)malloc((n+1)*sizeof(Olink))))

exit(overflow);

M.rhead[]=M.chead[]=NULL;//初始化行列头指针向量;

//各行列链表为空链表

for(scanf(&i,&j,&e);i!

=0;scanf(&i,&j,&e))

{//按任意次序输入非零元

if(!

(p=(OLNode*)malloc(sizeof(OLNode))))

exit(overflow);

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->jright)

{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->idown)

p->down=q->down;q->down=p;

}//完成列插入

}//for

}//if

ReturnOK;

}//CreateSMatrix_OL

稀疏矩阵的压缩存储方法:

顺序存储结构——三元组顺序表

三元顺序表的定义

#defineMAXSIZE20

typedefstruct{

inti,j;

Elemtypee;

}Triple;

typedefstruct{

Tripledata[MAXSIZE+1];

intmu,nu,tu;

}TSMatrix;

TSMatrixM;

矩阵运算

矩阵运算通常包括:

矩阵转置、矩阵加、矩阵减、矩阵乘、矩阵求逆等。

求矩阵的转置运算(最简单的运算)

对于一个m*n的矩阵Am*n,其转置矩阵是一个n*m的矩阵,为Bn*m,满足aij=bji,要实现三元组顺序表的转置,只要实现以下两点:

第一点:

把矩阵中元素的下标转置后,送入矩阵中;

第二点:

对矩阵中的元素按行序列序进行排序。

方法一:

按M的列序转置

即按T.data中三元组次序依次在M.data中找到相应的三元组进行转置。

为找到M中每一列所有非零元素,需对其三元组表M.data从第一行起扫描一遍。

由于M.data中以M行序为主序,所以由此得到的恰是T.data中应有的顺序

算法:

StatusTransposeSMatrix(TSMatrixM,TSMatrix&T)

{T.mu=M.nu;T.nu=M.mu;T.tu=M.tu;

if(T.tu)

{q=1;

for(col=1;col<=M.nu;++col)

for(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;

}

}

returnOK;

}

方法二:

稀疏矩阵的快速转置

算法思想是:

以M矩阵的三元组为中心,依次取出M.data中的每一个三元组,交换行列后,直接将其写入T.data合适的位置中。

实现:

引入两辅助数组

num[col]:

存储矩阵M中第col列中非零元个数

cpot[col]:

存储M中第col列第一个非零元在T.data中位置

显然有:

✡算法描述:

StatusFastTransposeSMatrix(TSMatrixM,TSMatrix&T)

{T.mu=M.nu;T.nu=M.mu;T.tu=M.tu;

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];

cpot[1]=1;

for(col=2;col<=M.nu;++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];

}

}

returnOK;

}

求矩阵乘运算

行逻辑链接的顺序表:

有时为了方便某些矩阵运算,我们在按行优先存储的三元组中,加入一个行表来记录稀疏矩阵中每行的非零元素在三元组表中的起始位置。

当将行表作为三元组表的一个新增属性加以描述时,我们就得到了稀疏矩阵的另一种顺序存储结构:

“带行链接信息”的三元组表。

其类型描述如下:

#definemaxsize100

typedefstruct{

tripledata[maxsize+1];//非零元三元组表

intrpos[maxrc+1];//各行第一个非零元的位置表

intn,m,t;//矩阵的行数、列数和非零元个数

}TSMatrix;

下面讨论两个稀疏矩阵相乘的例子,容易看出这种表示方法的优越性。

若设Q=M*N

其中,M是m1*n1矩阵,N是m2*n2矩阵。

常规的二维数组表示时转置的算法:

当n1=m2时,有:

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)。

行逻辑链接的顺序表从M和N求得Q

1、求乘积矩阵Q中元素

由矩阵乘法规则知:

Q(i,j)=M(i,1)×N(1,j)+M(i,2)×N(2,j)+…+M(i,n)×N(n,j)

这就是说,为求Q的值,只有M(i,k)与N(k,j)(即M元素的列与N元素的行相等的两项)才有相乘的机会,且当两项都不为零时,乘积中的这一项才不为零。

即只找到M.data中的j值和N.data中的i值相等的各对元素相乘即可。

2、相乘的基本操作是:

对于M中每个元素M.data[p],找到N中所有满足条件M.data[p].j=N.data[q].i的元素N.data[q],求得M.data[p].e和M.data[q].e的乘积。

即M11只有可能和N中第1行的非零元素相乘,M12只有可能和N中第2行的非零元素相乘,…,而同一行的非零元是相邻存放的,所以求Q11和Q12同时进行,当然只有Mik和Nkj(列号与行号相等)且均不为零(三元组存在)时才相乘,并且累加到Qij当中去。

为了便于N.data中寻找N中的第row行第一个非零元素,与前面类似,在此需引入num和rpos两个向量。

num[row]表示矩阵N中第row行的非零元素的个数;rpos[row]表示第row行的第一个非零元素在N.data中的位置。

于是有

rpos[1]=1

rpos[row]=rpos[row-1]+num[row-1]2≤row≤n

3、根据以上分析,稀疏矩阵的乘法运算的粗略步骤如下:

⑴初始化。

清理一些单元,准备按行顺序存放乘积矩阵;

⑵求N的num,rpos;

⑶做矩阵乘法。

将M.data中三元组的列值与N.data中三元组的行值相等的非零元素相乘,并将具有相同下标的乘积元素相加。

例如:

算法描述:

StatusMultSMatrix(TSMatrixM,TSMatrixN,TSMatrix&Q)

{//求矩阵乘积Q=M*N,采用行逻辑链接存储。

设M.nu=N.mu

if(M.nu!

=N.mu)

returnERROR;

if(M.tu*N.tu!

=0)

{//Q是非零矩阵

Q.mu=M.mu;Q.nu=N.nu;Q.tu=0;p=1//Q初始化

do

{

crow=M.data[p].i;//crow指示当前处理的行号

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

while(p<=M.tu&&M.data[p].i==crow)

{

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

for(q=N.rpos[brow];q

{

ccol=N.data[q].j;//乘积元素在Q中列号

ctemp[ccol]+=M.data[p].e*N.data[q].e;

}//forq

++p;

}//求得Q中第crow行的非零元

for(ccol=1;ccol<=Q.nu;++ccol)//压缩存储该行非零元

if(ctemp[ccol])

{++Q.tu;

Q.data[Q.tu]=(crow,ccol,ctemp[ccol]);

}//if

}//do

while(p<=M.tu);

}//if

returnOK;

}//MultSMatrix

相乘算法的时间复杂度就相当于O(m×p)。

求矩阵加运算

算法思路:

假设非空指针pa和pb分别指向矩阵A和B中行值相同的两个结点:

则主要处理过程为:

1、若pa==NULL或pa->j>pb->j,则需要在A矩阵的链表中插入一个值为bij的结点。

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

2、若pa->jj,则只要将pa指针往右推进一步。

3、若pa->j==pb->j且pa->e+pb->e!

=0,则只要将aij+bij的值送到pa所指结点的e域即可,其他所有域的值都不变。

4、若pa->j==pb->j且pa->e+pb->e==0,则需要在A矩阵的链表中删除pa所指的结点。

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

广义表

广义表的概念:

广义表也称为列表,是线性表的一种扩展,也是数据元素的有限序列。

记作:

LS=(d0,d1,d2,......dn-1)。

其中di既可以是单个元素,也可以是广义表。

说明

1)广义表的定义是一个递归定义,因为在描述广义表时又用到了广义表;

2)在线性表中数据元素是单个元素,而在广义表中,元素可以是单个元素,称为单元素(原子),也可以是广义表,称为广义表的子表;

3)n是广义表长度;

4)下面是一些广义表的例子

A=()空表,表长为0;

B=(a,(b,c,d))B的表长为2,两个元素分别为a和子表(b,c,d);

C=(e)C中只有一个元素e,表长为1;

D=(A,B,C,f)D的表长为4,它的前三个元素A,B,C广义表,第四个f是单元素;

E=(a,E)递归表.

5)若广义表不空,则可分成表头和表尾,反之,一对表头和表尾可唯一确定广义表

对非空广义表:

称第一个元素为L的表头,其余元素组成的表称为LS的表尾;

B=(a,(b,c,d))表头:

a表尾((b,c,d))

即HEAD(B)=a,TAIL(B)=((b,c,d)),

由表头、表尾定义可知:

任何一个非空列表其表头可能是原子,也可能是列表,而其表尾必定为列表。

C=(e)表头:

e表尾()

D=(A,B,C,f)表头:

A表尾(B,C,f)

运算可以嵌套,如:

HEAD(TAIL(B))=b,TAIL(TAIL(B))=(c,d)。

广义表的元素之间除了存在次序关系外,还存在层次关系。

如:

广义表的存储结构

由于广义表中数据元素可以具有不同结构,故难以用顺序结构表示广义表。

通常采用链表存储方式

如何设定链表结点?

广义表中的数据元素可能为单元素(原子)或子表,由此需要两种结点:

一种是表结点,用以表示广义表;一种是单元素结点,用以表示单元素(原子)。

按结点形式的不同,广义表的链式存储结构又可以分为不同的两种存储方式。

一种称为头尾表示法,另一种称为孩子兄弟表示法。

⒈头尾表示法

⒉孩子兄弟表示法

广义表链表结点的类型定义如下:

TypedefstructGLNode

{inttag;//标志域:

用于区分原子结点和表结点

union

{intatom;//原子结点的值域

structGLNode*hp;//表结点的表头指针域,

};

structGLNode*tp;//指向下一个元素的指针

}*GList;

 

广义表的基本操作的递归算法

广义表是递归结构,所以广义表的许多操作可以用递归算法实现

求广义表的深度intdepth(NODE*head)

广义表L=(α1,α2。

αn)的深度=广义表中括号重数

本章主要内容:

1、串的基本概念

2、串的存储结构

3、串的基本操作

4、串的模式匹配算法

5、串的应用----文本编辑

本章重点:

串的存储与基本操作。

本章难点:

串基本操作的灵活使用。

串的基本概念

串的定义:

串(或字符串)(String)是由零个或多个字符组成的有限序列。

一般记作

s=〃s0s1s2…sn-1〃(n≥0)

其中:

s为串名,用双引号括起来的字符序列是串的值;si(0≤i≤n-1)可以是字

母、数字或其它字符;双引号为串值的定界符,不是串的一部分;字符串字符的

数目n称为串的长度。

零个字符的串称为空串,通常以两个相邻的双引号来表示空

串,如:

s=〃〃,它的长度为零;仅由空格组成的的串称为空格串,如:

s=〃└┘〃;若串中含有空格,在计算串长时,空格应计入串的长度中,如:

s=〃I’mastudent〃的长度为13。

请读者注意,在C语言中,用单引号引起来的单个字符与单个字符的串是不同的,如s1='a'与s2=〃a〃两者是不同的,s1表示字符,而s2表示字符串。

串的基本操作

对于串的基本操作,许多高级语言均提供了相应的运算或标准库函数来实现。

下面仅介绍几种在C语言中常用的串运算,其它的串操作见的文件。

定义下列几个变量:

chars1[20]=“dirtreeformat”,s2[20]=“file.mem”;

chars3[30],*p;

intresult;

 

(1)串复制

strcpy(str1,str2)赋值:

把str2指向的字符串拷贝到str1中,返回str1。

库函数和形参说明如下:

char*strcpy(char*str1,char*str2)

例如:

strcpy(s3,s1);//s3=“dirtreeformat”

(2)求串长

strlen(str)求字符串的长度:

统计字符串str中字符的个数(不包括'\0'),返回字符的个数,若str为空串,则返回值为0。

库函数和形参说明如下:

unsignedintstrlen(char*str)

例如:

printf(“%d”,strlen(s1));输出13

(3)串比较

c函数:

intstrcmp(chars1,chars2);

该函数比较串s1和串s2的大小,当返回值小于0,等于0或大于0时分别表示

s1s2

例如:

result=strcmp(“baker”,”Baker”)result>0

result=strcmp(“12”,”12”);result=0

result=strcmp(“Joe”,”Joseph”);result<0

(4)串联接

charstrcat(char*to,char*from)

该函数将串from复制到串to的末尾,并且返回一个指向串to的开

始处的指针。

例如:

strcat(s3,”/”)

strcat(s3,s2);//s3=“dirtreeformat/file.mem”

(5)字符定位

charstrchr(char*s,charc);

该函数是在串s中寻找字符c第一次出现的位置,若找到则返回该

位置,否则返回NULL。

例如:

p=strchr(s2,”.”);p指向“file”之后的位置

(6)求子串

substr(s,t,i,k)

求子串的过程即为复制字符序列的过程,将串S中的第i个字符开始长度为k的字符串。

0<=i

(7)置换

Replace(s,t,v)

若主串s中存在与串t相等的子串则用v替换主串。

(8)插入

insert(s,i,t)

若0<=I<=length(s),则在串s的第i个字符之前插入串t。

自定

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

当前位置:首页 > 总结汇报 > 实习总结

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

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