第五章 数组和广义表
第五章数组和广义表
i(i-1)/2+j-1 (i≥j) k= 第五章数j组(j和-1广)义/2表+i-1 (i<j)
第五章数组和广义表
5.1 数组的类型定义 5.2 数组的顺序存储和实现 5.3 特殊矩阵的压缩存储 5.4 广义表
第五章数组和广义表
5.1 数组的定义和运算
数组是一种数据类型。从逻辑结构上看,数 组可以看成是一般线性表的扩充。二维数组可 以看成是线性表的线性表。例如:
Am×n=
a11 a12 … a1j ……
LO(0C ,0,..0.) , ci ji (Cn=L,ci-1=bi*ci,1<i≤n) i1
数组元素的存储位置是其下标的线性函数,由于计算各 个元素存储位置的时间相等,所以存储数组中任一元素的 时间也相等,称具有这一第五特章数点组和的广义存表 储结构为随机存储结构。
N维数组数据元素存储地址计算
i= (ai1,ai2, …,aij ,…,ain)。
B
‖
a11 a12 … a1j … a1n
1
……
…
Am×n= ai1 ai2 … aij … ain
i
……
…
am1 am2 … amj … amn
m
第五章数组和广义表
看一个二维数组的简单情况。
D={aij|0≤i≤b1-1,0≤j≤b2-1, aij∈ ElemType}
}
第五章数组和广义表
5.3 矩阵的压缩存储
• 压缩存储:为多个值相同的元素只分配一个存 储空间,对零元素不分配空间。
• 目的:节省存储空间 • 任务:压缩存储矩阵并使矩阵的运算有效进行。 • 矩阵的存储:二维数组 • 可压缩存储的矩阵有两类:
第5章数组与广义表
5.2 数组的顺序表示和实现
特点:用一组地址连续的存储单元按照某种规则存放数组 中的数据元素。 两种规则(顺序存储方式):
以行序为主(低下标优先)—将数组元素按行排列,第 i+1个行向量紧接在第i个行向量后。如:PASCAL、C。 以列序为主(高下标优先)—将数组元素按列排列,第 j+1个列向量紧接在第j个列向量后。如:FORTRAN。
am-1,0 a01 a11 …… am-1,1
……
按 列 序 为 主 序
a0,n-1 a1,n-1
……
am-1,n-1 am-1,n-1 ?A[2][3][2]以行序为主存储,写出其元素存放的先后顺序
5.2 数组的顺序表示和实现
计算数组任一元素(a j1 j2 ... jn )的地址需要的三要素: ①数组的起始地址(即基地址) ②数组维数和各维的长度; ③数组中每个元素所占的存储单元 已知二维数组Ab1*b2,每个元素占L个存储单元, LOC(0,0)是 数组第一个元素的起始地址,以行序为主存储,求LOC(i,j)。
N维数组数据元素存储地址计算
1、数组M[1..10][-1..6][[0..3],起始地址是1000,每个元素占3个 存储单元,数组元素个数是 ,M[2][4][2]的地址是 。 2、数组A[0 .. 8][1 .. 10]的成员由6个字节组成,存放a要 个字 节,a的第8行第5列占 个字节。按行序a[8][5]与按列序 起始地址相同。 (10-1+1)*(6-(-1)+1)*(3-0+1)=320 LOC(M[2][4][2])=1000+[(i-1)*b2*b3+(j-(-1))*b3+k]*l =1000+[32*(2-1)+4*(4+1)+2]*3=1162 LOC(a[8][5])=a(起始地址)+(i*n+(j-1))*l =a+(8*10+4)*6=a+504, LOC(a[3][10])=a(起始地址)+((j-1)*m+i)*l =a+(9*9+3)*6=a+504。
数据结构-第五章 数组与广义表-文档资料
上 三 角 矩 阵 下 三 角 矩 阵
a00 a10 a 20 an10
0 1 2
a01 a11 a21 an11
3 4
a02 a12 a22 an12
5
6 7
a0 n1 a1n1 a2 n1 an1n1
行 列 值 (row) (col) (value) 0 4 91 1 1 11 2 5 28 3 0 22 3 2 -6 5 1 17 5 3 39 6 0 16
用三元组表表示的稀疏矩阵及其转置
行 列 值 (row) (col) (value) 0 3 22 0 6 15 1 1 11 1 5 17 2 3 -6 3 5 39 4 0 91 5 2 28 行 列 值 (row) (col) (value) 0 4 91 1 1 11 2 5 28 3 0 22 3 2 -6 5 1 17 5 3 39 6 0 16
4 5 6 7 8 9 10
B a00 a01 a10 a11 a12 a21 a22 a23 … an-1n-2 an-1n-1
三对角矩阵中除主对角线及在主对角线上 下最临 近的两条对角线上的元素外,所有其它元素均为 0。总共有3n-2个非零元素。 将三对角矩阵A中三条对角线上的元素按行存放在 一维数组 B 中,且a00存放于B[0]。 在三条对角线上的元素aij 满足 0 i n-1, i-1 j i+1 在一维数组 B 中 A[i][j] 在第 i 行,它前面有 3*i-1 个非零元素, 在本行中第 j 列前面有 j-i+1 个,所 以元素 A[i][j] 在 B 中位置为 k = 2*i + j。
三对角矩阵的压缩存储
数据结构第五章数组和广义表
typedef elemtype Array1[n]; typedef Array1 Array2[m]; 同理,可以用 n-1 维数组的数据类型来定义 n 维数组。
8
第8页
5.2 数组的顺序存贮结构
一、数组的顺序表示和实现
(1) 类型特点 ① 只有引用型操作,一般不作插入或删除操作; ② 数组是多维的结构,而存储空间是一个一维的结构。
第 17 页
(2) 压缩存储的有关概念 ① 压缩存储:为多个值相同的元素分配一个存储空间,对零 元不分配空间。 ② 特殊矩阵:值相同的元素或零元素在矩阵中的分布有一定 规律。 ③ 稀疏矩阵:值相同的元素或者零元素在矩阵中的分布无规 律。
第 18 页
(3) 特殊矩阵
① 概念:若n阶矩阵 A 中的元满足:aij=aji 1≤i,j≤n,则 称为n 阶对称矩阵。
第1页
第五章 数组和广义表
5. 1 数组的定义 5.2 数组的顺序存储结构 5. 3 矩阵的压缩存储 5. 4 广义表的定义 5.5 广义表的存储结构
第2页
第五章 数组和广义表
前4章介绍的数据结构共同特点: ▲ 都属于线性数据结构; ▲ 每种数据结构中的数据元素,都作为原子数据, 不再进行分解; 本章讨论的两种数据结构:数组和广义表,其共 同特点是: ▲ 从逻辑结构上看它们,可看成是线性结构的一 种扩展; ▲ 数据元素本身也是一个数据结构;
4
第4页
(2) 二维数组的解释
二维数组中的每个元素都受两个 线性关系的约束,即行关系和列关系, 在每个关系中,每个元素aij都有且仅 有一个直接前趋,都有且仅有一个直 接后继。
aa … 00 01
《数据结构——用C语言描述(第二版)》第5章 数组和广义表
第五章 数组和广义表
在压缩存储时,矩阵中值相同的元素C可共享一个存储空间,元素 为零则可不必分配空间,而其余的元素有 n(n+1)/2个,因此三角矩阵 可用一维数组M[n×(n+1)/2+1]来存储,其中常数C放在数组的最后一 个下标变量中。
假设A和B矩阵分别用matrix型指针变量a和b表示,矩阵的转置可以 按以下进行:由于B的行是A的列,所以可按照b->data三元组表的次序在 a->data中找到相应的三元组进行转置,即可按a->data的列序转置,所得 到的转置矩阵B的三元组表b->data必定是按行优先存放的。因此,可以对 三元组表a->data从第一行起扫描,找到A的每一列中所有的非零元素,就 可以实现转置。
LOC ( aij ) =LOC ( a00) +(i×n+j) × c 同理可推导出以列为主序优先存储时数据元素a i j 的存储地址,其计算公式 为:
LOC( a i j ) =LOC( a00 ) +( j × n +i ) × c 对于三维数组Am×n×p而言,若以行为主序优先存储时,则其数据元 素aijk的存储地址可为: LOC ( a i j k) =LOC ( a000) +[ i × m×p +j ×p +k] × c 对于一般的二维数组A[c1…d1,c2…d2]而言,此处c1,c2的值不一定是 0,a i j 的地址为: LOC ( a i j ) =LOC ( a c 1 c 2 ) +[ ( i – c 1 )* ( d 2 – c 2 +1) +j – c 2 ] * c
数组和广义表 数据结构
3.建立广义表的存储结构 假定广义表中的元素类型ElemType为chai类型,每个原子的值被限 定为英文字母。并假定广义表是一个表达式,其格式为:元素之间用一 个逗号分隔,表元素的起止符号分别为左、右圆括号,空表在其圆括号 内不包含任何字符。例如“(a,(b, c, d))”就是一个符合上述规定的广 义表格式。 建立广义表存储结构的算法同样是一个递归算法。该算法使用一个 具有广义表格式的字符串参数s,返回由它生成的广义表存储结构的头结 点指针h。在算法的执行过程中,需要从头到尾扫描s的每一个字符。当 碰到左括号时,表明它是一个表元素的开始,则应建立一个由h指向的表 结点,并用它的sublist域作为子表的表头指针进行递归调用,来建立子 表的存储结构;当碰到一个英文字母时,表明它是一个原子,则应建立 一个由h指向的原子结点;当碰到一个“)”字符时,表明它是一个空表, 则应置h为空。当建立了一个由h指向的结点后,接着碰到逗号字符时, 表明存在后继结点,需要建立当前结点(即由h指向的结点)的后继表; 当碰到右括号或分号字符时,表明当前所处理的表已结束,应该置当前 结点的link域为空。 4.输出广义表 5.广义表的复制
广义表的转换过程
为了使子表和原子两类结点既能在形式上保持一致,又能进
行区别,可采用如下结构形式:
其中,tag域为标志字段,用于区分两类结点。sublist或data
域由tag决定。若tag=0,表示该结点为原子结点,则第二个 域为data,存放相应原子元素的信息;若tag=l,表示该结点 为表结点,则第二个域为sublist,存放相应子表第一个元素 对应结点的地址。link域存放与本元素同一层的下一个元素所 在结点的地址,当本元素是所在层的最后一个元素时,link域 为NULL。 例:前面的广义表C的存储结构如下图所示(很多《数据结构 公教科书上称之为带表头结点的广义表的链表存储结构
第5章 数组和广义表
第五章数组和广义表讲课提要【主要内容】1.多维数组的顺序存储结构2.特殊矩阵的压缩存储3.广义表的定义及其与线性表的关系4.广义表的存储结构5.广义表运算实现中递归的应用【教学目标】1.掌握多维数组的顺序存储结构2.掌握特殊矩阵的压缩存储方法3.掌握广义表的定义及其与线性表的关系4.掌握广义表的存储结构5.了解广义表运算实现中递归的应用学习指导1.多维数组的顺序存储结构对于多维数组,有两种存储方式:一是以行为主序(或先行后列)的顺序存放,如BASIC、PASCAL、C等程序设计语言中用的是以行为主的顺序分配,即一行分配完了接着分配下一行。
另一种是以列为主序(先列后行)的顺序存放,如FORTRAN语言中,用的是以列为主序的分配顺序,即一列一列地分配。
以行为主序的分配规律是:最右边的下标先变化,即最右下标从小到大,循环一遍后,右边第二个下标再变,…,从右向左,最后是左下标。
以列为主序分配的规律是:最左边的下标先变化,即最左下标从小到大,循环一遍后,左边第二个下标再变,…,从左向右,最后是右下标。
不论按何种方式存储,只要确定了数组的首地址以及每个数组元素所占用的单元数,就可以将数组元素的存储地址表示为其下标的线性函数。
设有m×n二维数组A mn,以“以行为主序”的分配为例,按照元素的下标确定其地址的计算方法如下。
设数组的基址为LOC(a11),每个数组元素占据L个地址单元,计算a ij 的物理地址的函数为:LOC(a ij) = LOC(a11) + ( (i-1)*n + j-1 ) * L同理,对于三维数组A mnp,即m×n×p数组,对于数组元素a ijk其物理地址为:LOC(a ijk)=LOC(a111)+( ( i-1) *n*p+ (j-1)*p +k-1) )*L注意:在C语言中,数组中每一维的下界定义为0,则:LOC(a ij) = LOC(a00) + ( i*n + j ) * L【例4-1】二维数组A的每一个元素是由6个字符组成的串,其行下标i=0,1,…,8,列下标j=1,2,…,10。
第5章数组和广义表
A.contants[i]=A.bounds[i+1]*A.constants[i+1]; return ok; }
第5章 数组和广义表
Status DestoryArray(Array &A){ //销毁数组 if(!A.base) return ERROR; free(A.base); A.base=NULL; if(!A.bounds) return ERROR; free(A.bounds); A.bounds=NULL; if(!A.contants) return ERROR; free(A.contants) A.contants=NULL; return ok;
(3) Value(A,&e, index1, …, indexn): 若下标合法,则 用e返回数组A中由index1, …, indexn所指定的元素的值。
(4) Assign(&A,e,indexl,…indexn):若各下标不超界, 则将e赋值为所指定的A的元素值,并返回OK。 。
第5章 数组和广义表
三维数组A(1..r , 1..m , 1..n)可以看成是r个m×n的二维数组,
如图5.5所示。
n
m n
r j- 1
m
k- 1
图5.5 三维数组看成r个m×n的二维数组
第5章 数组和广义表
假定每个元素占一个存储单元,采用以行为主序的方法存 放,即行下标r变化最慢, 纵下标n变化最快。 首元素a111的地 址为Loc[1, 1, 1],求任意元素aijk的地址。
第5章 数组和广义表
以上我们以二维数组为例介绍了数组的结构特性,实际 上数组是一组有固定个数的元素的集合。也就是说,一旦定 义了数组的维数和每一维的上下限,数组中元素的个数就固 定了。 例如二维数组A3×4,它有3行、4列,即由12个元素组 成。由于这个性质,使得对数组的操作不像对线性表的操作 那样可以在表中任意一个合法的位置插入或删除一个元素。 对于数组的操作一般只有两类:
数据结构第5章数组与广义表
一个稀疏矩阵里存在大量的零元素,若 以常规方法,即以二维数组来存储稀疏矩 阵时产生如下问题: 1) 零值元素占了很多空间; 2) 如果进行计算,则会进行很多和零值 的运算,如是除法,还需判别除数是否为 零。
2 三角矩阵 以主对角线划分,三角矩阵有上三角 和下三角两种。如图所示。其中(a)图为下 三角矩阵:主对角线以上均为同一个常 数;(b)图为上三角矩阵,主对角线以下均 为同一个常数。
(1) 下三角矩阵 三角矩阵中的重复元素c可共享一个 存储空间,其余的元素正好有n(n+1)/2 个,因此,三角矩阵可压缩存储到向量 SA[0…n(n+1)/2]中,其中c存放在向量的 最后1个分量SA[n(n+1)/2]中。 该存储方式可节约n*(n-1)/2-1个存储 单元。
传统矩阵的转置算法为: for(col=1; col<=n ;++col) for(row=0 ; row<=m ;++row) b[col][row]=a[row][col] ;
时间复杂度为O(n×m) 当非零元素的个数tn和m×n同数量级时,算法 TransMatrix的时间复杂度为O(m×n2)。
以“行优先顺序”存储: (1) 第1行中的每个元素对应的(首)地址是: LOC[a1j]=LOC[a11]+(j-1) ×L (2) 第2行中的每个元素对应的(首)地址是: LOC[a2j]=LOC[a11]+n×L +(j-1) ×L (3) 第m行中的每个元素对应的(首)地址是: LOC[amj]=LOC[a11]+(m-1) n×L +(j-1) ×L
第5章 数组和广义表总结
0603
2090
B5×4 = 0 8 0 0
0000
row col val
4000
0 12
0 44
1 06
1 28
2 19
3 03
现在,要通过A的 三元组表求其转置 矩阵B的三元组表。
row col val
0 16 0 33 1 02 1 29 2 18 4 04
16
三元组表的转置
方法1:设矩阵A是m行、n列、t个非0元素 从头到尾扫描A,将第0列的元素依次放入B(行号列号互换); 从头到尾扫描A,将第1列的元素依次放入B(行号列号互换); ...... 从头到尾扫描A,将第n-1列的元素依次放入B(行号列号互换); 扫描A几趟? 矩阵A的列数n
}tritype;
typedef struct { //三元组表
tritype data[MAXSIZE]; //三元组表存储空间
int mu, nu, tu;
//原矩阵的行数、列数、非0元素个数
}Tsmtype, *Tsmlink;
//三元组表说明符
14
三元组表
例 5.3 矩阵的转置。 求矩阵A的转置矩阵B,算法很简单:
ArrayInit(&A, n, d1, d2, ..., dn) ArrayDestroy(&A)
ArrayGet(A, i1, ..., in, &e)
ArrayAssign(&A, i1, ..., in, e) }ADT Array;
数组的基本操作一般不包括插入和删除
4
5.2 数组的存储结构
存储空间是在程序执行时动态分配的
n维数组A[u1][u2]…[un]
数据结构第五章 数组和广义表
5.3.1
特殊矩阵
1、对称矩阵 在一个n阶方阵A中,若元素满足下述性质: aij = aji 1≤i,j≤n 则称A为对称矩阵。 a11 1 5 1 3 7 a21 a 22 5 0 8 0 0 a31 a32 a33 1 8 9 2 6 ……………….. 3 0 2 5 1 an 1 a n 2 a n 3 …a n n 7 0 6 1 3
第5章
数组和广义表
5.1 数组的定义
5.2 数组的顺序表示和实现
5.3 矩阵的压缩存储
5.3.1 特殊矩阵
5.3.2 稀疏矩阵
5.4 广义表的定义
5.1 数组的定义
数组-----线性表的扩展 A =(a0,a1,a2,…,an-1)
a00 a10 ┇ Am×n= ai0 ┇ am-1,0 a01 … a0j … a11 … a1j … ┇ ai2 … aij … ┇ am-1,2 … am-1,j … a0,n-1 a1,n-1 ai,n-1 am-1,n-1 α0 α1 ┇ Am×n= α i ┇ α m-1
Assign( &A, e, index1, ..., indexn) 赋值操作 初始条件:A是n维数组,e为元素变量,随后是n个下标值。 操作结果:若下标不超界,则将e的值赋给所指定的A的元 素,并返回OK。 对于数组来说一旦维数确定了,每个元素的下标确定了, 那么整个数组就确定了,这样的一个数组结构除了能改变 某元素的值,其他的不能再改变。
5.2 数组的顺序表示和实现
数组类型特点: 1) 只有引用型操作,没有加工型操作; 2) 数组是多维的结构,而存储空间是一个一维的结构。 有两种顺序映象的方式。
有两种顺序映像方法: 1)以行序为主序(行优先,先行后列):先存储行号较小 的元素,行号相同者先存储列号较小的元素;
数据结构 第五章 数组和广义表
则行优先存储时的地址公式为: LOC(aij)=LOC(ac1,c2)+[(i-c1)*(d2-c2+1)+j-c2)]*L
数组基址
aij之前的行
数
总列数,即 第2维长度
aij本行前面
的元素个数
单个元素 长度
例2一个二维数组A,行下标的范围是1到6,列下标的范围是0
到7,每个数组元素用相邻的6个字节存储,存储器按字节编址。 288 个字节。 那么,这个数组的体积是
2.若对n阶对称矩阵A以行序为主序方式将其下三 角形的元素(包括主对角线上所有元素)依次存 放于一维数组B[1..(n(n+1))/2]中,则在B 中确定aij(i<j)的位置k的关系为( )。 A. i*(i-1)/2+j B. j*(j-1)/2+i C. i*(i+1)/2+j D. j*(j+1)/2+i
维界虽未变,但此时的a[32,58]不 再是原来的a[32,58]
例5:假设有三维数组A7×9×8,每个元素用相邻的6个字节存
储,存储器按字节编址。已知A的起始存储位置(基地址)为 1000,末尾元素A[6][8][7]的第一个字节地址为多少?若按 高地址优先存储时,元素A[4][7][6]的第一个字节地址为多 少? 答: 末尾元素A[6][8][7]的第1个字节地址= 1000 +(9*8*6+8*8+7)*6=4018 提示:将第1维看作“页码”,后面两维就是每页上的二维数组 。 计算地址 的意义: 只要计算出任一数组元素的地址,就 能对其轻松地进行读写操作!
4.已知数组A[0..9,0..9]的每个元素占5个存储 单元,将其按行优先次序存储在起始地址为 1000的连续的内存单元中,则元素A[6,8]的 地址为_________
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
第五章数组和广义表5.18void RSh(int A[n],int k)//把数组A的元素循环右移k位,只用一个辅助存储空间{for(i=1;i<=k;i++)if(n%i==0&&k%i==0) p=i;//求n和k 的最大公约数pfor(i=0;i<p;i++){j=i;l=(i+k)%n;temp=A[i];while(l!=i){A[j]=temp;temp=A[l];A[l]=A[j];j=l;l=(j+k)%n;}// 循环右移一步A[i]=temp;}//for}//RSh分析:要把A的元素循环右移k位,则A[0]移至A[k],A[k]移至A[2k]......直到最终回到A[0].然而这并没有全部解决问题,因为有可能有的元素在此过程中始终没有被访问过,而是被跳了过去.分析可知,当n和k的最大公约数为p 时,只要分别以A[0],A[1],...A[p-1]为起点执行上述算法,就可以保证每一个元素都被且仅被右移一次,从而满足题目要求.也就是说,A的所有元素分别处在p个"循环链"上面.举例如下:n=15,k=6,则p=3.第一条链:A[0]->A[6],A[6]->A[12],A[12]->A[ 3],A[3]->A[9],A[9]->A[0].第二条链:A[1]->A[7],A[7]->A[13],A[13]->A[第三条链:A[2]->A[8],A[8]->A[14],A[14]->A[ 5],A[5]->A[11],A[11]->A[2].恰好使所有元素都右移一次.虽然未经数学证明,但作者相信上述规律应该是正确的.5.19void Get_Saddle(int A[m][n])//求矩阵A 中的马鞍点{for(i=0;i<m;i++){for(min=A[i][0],j=0;j<n;j++)if(A[i][j]<min) min=A[i][j]; //求一行中的最小值for(j=0;j<n;j++)if(A[i][j]==min) //判断这个(些)最小值是否鞍点{for(flag=1,k=0;k<m;k++)if(min<A[k][j]) flag=0;if(flag)printf("Found a saddleelement!\nA[%d][%d]=%d",i,j,A[i][j]); }}//for}//Get_Saddle5.20本题难度极大,故仅探讨一下思路.这一题的难点在于,在多项式的元数m未知的情况下,如何按照降幂次序输出各项.可以考虑采取类似于层序遍历的思想,从最高次的项开始,依次对其每一元的次数减一,入一个队列.附设访问标志visited以避免重复.5.21A,TSMatrix B,TSMatrix &C)//三元组表示的稀疏矩阵加法{C.mu=A.mu;C.nu=A.nu;C.tu=0;pa=1;pb=1;pc=1;for(x=1;x<=A.mu;x++) //对矩阵的每一行进行加法{while(A.data[pa].i<x) pa++;while(B.data[pb].i<x) pb++;while(A.data[pa].i==x&&B.data[pb].i ==x)//行列值都相等的元素{if(A.data[pa].j==B.data[pb].j){ce=A.data[pa].e+B.data[pb].e;if(ce) //和不为0{C.data[pc].i=x;C.data[pc].j=A.data[pa].j;C.data[pc].e=ce;pa++;pb++;pc++;}}//ifelse if(A.data[pa].j>B.data[pb].j){C.data[pc].i=x;C.data[pc].j=B.data[pb].j;C.data[pc].e=B.data[pb].e;pb++;pc++;}else{C.data[pc].i=x;C.data[pc].j=A.data[pa].j;C.data[pc].e=A.data[pa].epa++;pc++;}}//whilewhile(A.data[pa]==x) //插入A中剩余的元素(第x行) C.data[pc].i=x;C.data[pc].j=A.data[pa].j;C.data[pc].e=A.data[pa].epa++;pc++;}while(B.data[pb]==x) //插入B中剩余的元素(第x行){C.data[pc].i=x;C.data[pc].j=B.data[pb].j;C.data[pc].e=B.data[pb].e;pb++;pc++;}}//forC.tu=pc;}//TSMatrix_Add5.22void TSMatrix_Addto(TSMatrix&A,TSMatrix B)//将三元组矩阵B加到A上{for(i=1;i<=A.tu;i++)A.data[MAXSIZE-A.tu+i]=A.data[i];/把A的所有元素都移到尾部以腾出位置pa=MAXSIZE-A.tu+1;pb=1;pc=1;for(x=1;x<=A.mu;x++) //对矩阵的每一行进行加法{while(A.data[pa].i<x) pa++;while(B.data[pb].i<x) pb++;while(A.data[pa].i==x&&B.data[pb].i ==x)//行列值都相等的元素{if(A.data[pa].j==B.data[pb].j){ne=A.data[pa].e+B.data[pb].e;if(ne) //和不为0{A.data[pc].j=A.data[pa].j;A.data[pc].e=ne;pa++;pb++;pc++;}}//ifelse if(A.data[pa].j>B.data[pb].j){A.data[pc].i=x;A.data[pc].j=B.data[pb].j;A.data[pc].e=B.data[pb].e;pb++;pc++;}else{A.data[pc].i=x;A.data[pc].j=A.data[pa].j;A.data[pc].e=A.data[pa].epa++;pc++;}}//whilewhile(A.data[pa]==x) //插入A中剩余的元素(第x行){A.data[pc].i=x;A.data[pc].j=A.data[pa].j;A.data[pc].e=A.data[pa].epa++;pc++;}while(B.data[pb]==x) //插入B中剩余的元素(第x行){A.data[pc].i=x;A.data[pc].j=B.data[pb].j;A.data[pc].e=B.data[pb].e;pb++;pc++;}}//forA.tu=pc;for(i=A.tu;i<MAXSIZE;i++)A.data[i]={0,0,0}; //清除原来的A中记}//TSMatrix_Addto5.23typedef struct{int j;int e;} DSElem;typedef struct{DSElem data[MAXSIZE];int cpot[MAXROW];//这个向量存储每一行在二元组中的起始位置 int mu,nu,tu;} DSMatrix; //二元组矩阵类型Status DSMatrix_Locate(DSMatrix A,int i,int j,int &e)//求二元组矩阵的元素A[i][j]的值e{for(s=cpot[i];s<cpot[i+1]&&A.data[s].j !=j;s++);//注意查找范围if(A.data[s].i==i&&A.data[s].j==j) //找到了元素A[i][j]{e=A.data[s];return OK;}return ERROR;}//DSMatrix_Locate5.24typedef struct{int seq; //该元素在以行为主序排列时的序号int e;} SElem;typedef struct{SElem data[MAXSIZE];int mu,nu,tu;组矩阵类型Status SMatrix_Locate(SMatrix A,int i,int j,int &e)//求单下标二元组矩阵的元素A[i][j]的值e{s=i*A.nu+j+1;p=1;while(A.data[p].seq<s) p++; //利用各元素seq值逐渐递增的特点if(A.data[p].seq==s) //找到了元素A[i][j]{e=A.data[p].e;return OK;}return ERROR;}//SMatrix_Locate5.25typedef enum{0,1} bool;typedef struct{int mu,nu;int elem[MAXSIZE];bool map[mu][nu];} BMMatrix; //用位图表示的矩阵类型void BMMatrix_Add(BMMatrixA,BMMatrix B,BMMatrix &C)//位图矩阵的加法{C.mu=A.mu;C.nu=A.nu;pa=1;pb=1;pc=1;for(i=0;i<A.mu;i++) //每一行的相加 for(j=0;j<A.nu;j++) //每一个元素的相加{if(A.map[i][j]&&B.map[i][j]&&(A.e lem[pa]+B.elem[pb]))//结果不为0{ ];C.map[i][j]=1;pa++;pb++;pc++;}else if(A.map[i][j]&&!B.map[i][j]) {C.elem[pc]=A.elem[pa];C.map[i][j]=1;pa++;pc++;}else if(!A.map[i][j]&&B.map[i][j]) {C.elem[pc]=B.elem[pb];C.map[i][j]=1;pb++;pc++;}}}//BMMatrix_Add5.26void Print_OLMatrix(OLMatrix A)//以三元组格式输出十字链表表示的矩阵{for(i=0;i<A.mu;i++){if(A.rhead[i])for(p=A.rhead[i];p;p=p->right) //逐次遍历每一个行链表printf("%d %d %d\n",i,p->j,p->e; }}//Print_OLMatrix5.27void OLMatrix_Add(OLMatrix&A,OLMatrix B)//把十字链表表示的矩阵B加到A上{for(j=1;j<=A.nu;j++) cp[j]=A.chead[j]; //向量cp存储每一列当前最后一个元for(i=1;i<=A.mu;i++){pa=A.rhead[i];pb=B.rhead[i];pre=NU LL;while(pb){if(pa==NULL||pa->j>pb->j) //新插入一个结点{p=(OLNode*)malloc(sizeof(OLNo de));if(!pre) A.rhead[i]=p;else pre->right=p;p->right=pa;pre=p;p->i=i;p->j=pb->j;p->e=pb->e; //插入行链表中if(!A.chead[p->j]){A.chead[p->j]=p;p->down=NULL;}else{while(cp[p->j]->down)cp[p->j]=cp[p->j]->down;p->down=cp[p->j]->down;cp[p->j]->down=p;}cp[p->j]=p; //插入列链表中}//ifelse if(pa->j<pb->j){pre=pa;pa=pa->right;} //pa右移一步else if(pa->e+pb->e){pa->e+=pb->e;pre=pa;pa=pa->right;pb=pb->right;} //直接相加 {if(!pre) A.rhead[i]=pa->right;else pre->right=pa->right;p=pa;pa=pa->right; //从行链表中删除if(A.chead[p->j]==p)A.chead[p->j]=cp[p->j]=p->down; else cp[p->j]->down=p->down; //从列链表中删除free (p);}//else}//while}//for}//OLMatrix_Add分析:本题的具体思想在课本中有详细的解释说明.5.28void MPList_PianDao(MPList &L)//对广义表存储结构的多元多项式求第一变元的偏导{for(p=L->hp->tp;p&&p->exp;pre=p,p= p->tp){if(p->tag) Mul(p->hp,p->exp);else p->coef*=p->exp; //把指数乘在本结点或其下属结点上p->exp--;}pre->tp=NULL;if(p) free (p); //删除可能存在的常数项}//MPList_PianDaovoid Mul(MPList &L,int x)//递归算法,对多元多项式L乘以x{for(p=L;p;p=p->tp){else Mul(p->hp,x);}}//Mul5.29void MPList_Add(MPList A,MPList B,MPList &C)//广义表存储结构的多项式相加的递归算法{C=(MPLNode*)malloc(sizeof(MPLNo de));if(!A->tag&&!B->tag) //原子项,可直接相加{C->coef=A->coef+B->coef;if(!C->coef){free(C);C=NULL;}}//ifelse if(A->tag&&B->tag) //两个多项式相加{p=A;q=B;pre=NULL;while(p&&q){if(p->exp==q->exp){C=(MPLNode*)malloc(sizeof(MP LNode));C->exp=p->exp;MPList_Add(A->hp,B->hp,C->hp); pre->tp=C;pre=C;p=p->tp;q=q->tp;}else if(p->exp>q->exp){C=(MPLNode*)malloc(sizeof(MP LNode)); C->hp=A->hp;pre->tp=C;pre=C;p=p->tp;}else{C=(MPLNode*)malloc(sizeof(MP LNode));C->exp=q->exp;C->hp=B->hp;pre->tp=C;pre=C;q=q->tp;}}//whilewhile(p){C=(MPLNode*)malloc(sizeof(MPL Node));C->exp=p->exp;C->hp=p->hp;pre->tp=C;pre=C;p=p->tp;}while(q){C=(MPLNode*)malloc(sizeof(MPL Node));C->exp=q->exp;C->hp=q->hp;pre->tp=C;pre=C;q=q->tp;} //将其同次项分别相加得到新的多项式,原理见第二章多项式相加一题 }//else ifelse if(A->tag&&!B->tag) //多项式和常数项相加{x=B->coef;for(p=A;p->tp->tp;p=p->tp);if(p->tp->exp==0) p->tp->coef+=x; //当多项式中含有常数项时,加上常数项{free(p->tp);p->tp=NULL;}else{q=(MPLNode*)malloc(sizeof(MPL Node));q->coef=x;q->exp=0;q->tag=0;q->tp=NULL;p->tp=q;} //否则新建常数项,下同}//else ifelse{x=A->coef;for(p=B;p->tp->tp;p=p->tp);if(p->tp->exp==0) p->tp->coef+=x; if(!p->tp->coef){free(p->tp);p->tp=NULL;}else{q=(MPLNode*)malloc(sizeof(MPL Node));q->coef=x;q->exp=0;q->tag=0;q->tp=NULL;p->tp=q;}}//else}//MPList_Add5.30int GList_Getdeph(GList L)//求广义表深度的递归算法{if(!L->tag) return 0; //原子深度为0 else if(!L) return 1; //空表深度为1 n=GList_Getdeph(L->ptr.tp);return m>n?m:n;}//GList_Getdeph5.31void GList_Copy(GList A,GList &B)//复制广义表的递归算法{if(!A->tag) //当结点为原子时,直接复制{B->tag=0;B->atom=A->atom;}else //当结点为子表时{B->tag=1;if(A->ptr.hp){B->ptr.hp=malloc(sizeof(GLNode)); GList_Copy(A->ptr.hp,B->ptr.hp); } //复制表头if(A->ptr.tp){B->ptr.tp=malloc(sizeof(GLNode)); GList_Copy(A->ptr.tp,B->ptr.tp); } //复制表尾}//else}//GList_Copy5.32int GList_Equal(GList A,GList B)//判断广义表A和B是否相等,是则返回1,否则返回0{ //广义表相等可分三种情况:if(!A&&!B) return 1; //空表是相等的 if(!A->tag&&!B->tag&&A->atom==B ->atom) return 1;//原子的值相等if(A->tag&&B->tag)if(GList_Equal(A->ptr.hp,B->ptr.hp)return 1; //表头表尾都相等return 0;}//GList_Equal5.33void GList_PrintElem(GList A,int layer)//递归输出广义表的原子及其所在层次,layer表示当前层次{if(!A) return;if(!A->tag)printf("%d %d\n",A->atom,layer);else{GList_PrintElem(A->ptr.hp,layer+1); GList_PrintElem(A->ptr.tp,layer); //注意尾表与原表是同一层次}}//GList_PrintElem5.34void GList_Reverse(GList A)//递归逆转广义表A{if(A->tag&&A->ptr.tp) //当A不为原子且表尾非空时才需逆转{D=C->ptr.hp;A->ptr.tp->ptr.hp=A->ptr.hp;A->ptr.h p=D; //交换表头和表尾GList_Reverse(A->ptr.hp);GList_Reverse(A->ptr.tp); //逆转表头和表尾}}//GList_Reverse5.35Status Create_GList(GList &L)//根据输入创建广义表L,并返回指针 scanf("%c",&ch);if(ch==' '){L=NULL;scanf("%c",&ch);if(ch!=')') return ERROR;return OK;}L=(GList)malloc(sizeof(GLNode));L->tag=1;if(isalphabet(ch)) //输入是字母{p=(GList)malloc(sizeof(GLNode)); //建原子型表头p->tag=0;p->atom=ch;L->ptr.hp=p;}else if(ch=='(')Create_GList(L->ptr.hp); //建子表型表头else return ERROR;scanf ("%c",&ch);if(ch==')') L->ptr.tp=NULL;else if(ch==',') Create_GList(L->ptr.tp); //建表尾else return ERROR;return OK;}//Create_GList分析:本题思路见书后解答.5.36void GList_PrintList(GList A)//按标准形式输出广义表{if(!A) printf("()"); //空表else if(!A->tag)printf("%d",A->atom);//原子else{printf("(");if(A->ptr.tp){printf(",");GList_PrintList(A->ptr.tp);} //只有当表尾非空时才需要打印逗号printf(")");}//else}//GList_PrintList5.37void GList_DelElem(GList &A,int x)//从广义表A中删除所有值为x的原子{if(A->ptr.hp->tag)GList_DelElem(A->ptr.hp,x);elseif(!A->ptr.hp->tag&&A->ptr.hp->atom= =x){q=A;A=A->ptr.tp; //删去元素值为x的表头free(q);GList_DelElem(A,x);}if(A->ptr.tp)GList_DelElem(A->ptr.tp,x);}//GList_DelElem5.39void GList_PrintElem_LOrder(GList A)//按层序输出广义表A中的所有元素{InitQueue(Q);for(p=L;p;p=p->ptr.tp) EnQueue(Q,p); while(!QueueEmpty(Q)){DeQueue(Q,r); elsefor(r=r->ptr.hp;r;r=r->ptr.tp) EnQueue(Q,r);}//while}//GList_PrintElem_LOrder分析:层序遍历的问题,一般都是借助队列来完成的,每次从队头取出一个元素的同时把它下一层的孩子插入队尾.这是层序遍历的基本思想.。