资源预览内容
第1页 / 共112页
第2页 / 共112页
第3页 / 共112页
第4页 / 共112页
第5页 / 共112页
第6页 / 共112页
第7页 / 共112页
第8页 / 共112页
第9页 / 共112页
第10页 / 共112页
亲,该文档总共112页,到这儿已超出免费预览范围,如果喜欢就下载吧!
资源描述
第五章第五章 数组和广义表数组和广义表 数组和广义表可看成是数组和广义表可看成是一种特殊的线性表,其特一种特殊的线性表,其特殊在于殊在于:表中的数据元素表中的数据元素本身也是一种线性表本身也是一种线性表 5.1 数组的定义 数组是我们最熟习的数据类型,在早期的高级言语中,数组是独一可供运用的数据类型。由于数组中各元素具有一致的类型,并且数组元素的下标普通具有固定的上界和下界,因此,数组的处置比其它复杂的构造更为简单。多维数组是向量的推行。例如,二维数组: a11 a12 a1n a21 a22 a2n am1 am2 amn Amn= 可以看成是m由个行向量组成的向量,也可以看成是n个列向量组成的向量。 数组一旦被定义,它的维数和维界就不再改动。因此,除了构造的初始化和销毁之外,数组只需存取元素和修正元素值的操作。 5.2 数组的顺序表示和实现 由于计算机的内存储构造是一维的,因此用一维内存来表示多维数组,就必需按某种次序将数组元素排成一列序列,然后将这个线性序列存放在存储器中。 又由于对数组普通不做插入和删除操作,也就是说,数组一旦建立,构造中的元素个数和元素间的关系就不再发生变化。因此,普通都是采用顺序存储的方法来表示数组。 5.1 数数组组的的类类型定型定义义5.3 稀疏矩稀疏矩阵阵的的紧缩紧缩存存储储 5.2 数数组组的的顺顺序表示和序表示和实现实现5.4 广广义义表的表的类类型定型定义义5.5 广广义义表的表示方法表的表示方法5.6 广广义义表操作的表操作的递归递归函数函数5.1 数组的类型定义数组的类型定义ADT Array 数据数据对对象:象: Daj1,j2, .,ji,jn| ji =0,.,bi -1, i=1,2,.,n 数据关系:数据关系: RR1, R2, ., Rn Ri | 0 jk bk -1, 1 k n 且且k i, 0 ji bi -2, i=2,.,n ADT Array 根本操作根本操作:二维数组的定义二维数组的定义:数据数据对象象: : D = aij | 0ib1-1, 0 D = aij | 0ib1-1, 0 jb2-1jb2-1数据关系数据关系: : R = ROW, COL R = ROW, COL ROW = | 0ib1- ROW = | 0ib1-2, 0jb2-12, 0jb2-1 COL = | 0ib1- COL = | 0ib1-1, 0 jb2-21, 0 jb2-2根本操作:根本操作:InitArray(&A, n, bound1, ., boundn)DestroyArray(&A)Value(A, &e, index1, ., indexn)Assign(&A, e, index1, ., indexn) 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。5.2 数组的顺序表示和实现数组的顺序表示和实现 类类型特点型特点:1) 只需援用型操作,没有加工型操作;只需援用型操作,没有加工型操作;2) 数数组组是多是多维维的构造,而存的构造,而存储储空空间间是是 一个一一个一维维的构造。的构造。 有两种有两种顺顺序映象的方式序映象的方式:1)以行序以行序为为主序主序(低下低下标优标优先先);2)以列序以列序为为主序主序(高下高下标优标优先先)。例如:例如: 称为基地址或基址。以以“行序行序为主序的存主序的存储映象映象二维数组A中任一元素ai,j 的存储位置 LOC(i,j) = LOC(0,0) + (b2ij)a0,1a0,0a0,2a1,0a1,1a1,2a0,1a0,0a0,2a1,0a1,1a1,2L L 推行到普通情况,可得到 n 维数组数据元素存储位置的映象关系 称为 n 维数组的映象函数。数组元素的存储位置是其下标的线性函数。其中 cn = L,ci-1 = bi ci , 1 i n。LOC(j1, j2, ., jn ) = LOC(0,0,.,0) + ci ji i=1n假设 m 行 n 列的矩阵含 t 个非零元素,那么称 为稀疏因子。通常以为 0.05 的矩阵为稀疏矩阵。5.3 稀疏矩阵的紧缩存储稀疏矩阵的紧缩存储何谓稀疏矩阵? 以常规方法,即以二维数组表示高阶的稀疏矩阵时产生的问题:1) 零零值值元素占了很大空元素占了很大空间间;2) 计计算中算中进进展了很多和零展了很多和零值值的运算,的运算, 遇除法,遇除法,还还需判需判别别除数能否除数能否为为零。零。1) 尽能够少存或不存零值元素;处理问题的原那么处理问题的原那么:2) 尽能够减少没有实践意义的运算;3) 操作方便。 即: 能尽能够快地找到与 下标值(i,j)对应的元素, 能尽能够快地找到同 一行或同一列的非零值元。1) 特殊矩特殊矩阵阵 非零元在矩非零元在矩阵阵中的分布有一定中的分布有一定规规那么那么 例如例如: 三角矩三角矩阵阵 对对角矩角矩阵阵2) 随机稀疏矩随机稀疏矩阵阵 非零元在矩非零元在矩阵阵中随机出中随机出现现有两类稀疏矩阵:有两类稀疏矩阵:5.3.1特殊矩阵 所谓特殊矩阵是指非零元素或零元素的分布有一定规律的矩阵,下面我们讨论几种特殊矩阵的紧缩存储。1、对称矩阵 在一个n阶方阵A中,假设元素满足下述性质: aij=aji 0i,jn-1那么称A为对称矩阵。如图5.1便是一个5阶对称矩阵。 对称矩阵中的元素关于主对角线对称,故只需存储矩阵中上三角或下三角中的元素,让每两个对称的元素共享一个存储空间,这样,能节约近一半的存储空间。不失普通性,我们按“行优先顺序存储主对角线包括对角线以下的元素,其存储方式如下图: 1 5 1 3 7 a00 5 0 8 0 0 a10 a 11 1 8 9 2 6 a20 a21 a23 3 0 2 5 1 . 7 0 6 1 3 an-1 0 a n-1 1 a n-1 2 a n-1 n-1 图 5.1 对称矩阵 在这个下三角矩阵中,第i行恰有i+1个元素,元素总数为: n(n+1)/2 因此,我们可以按图中箭头所指的次序将这些元素存放在一个向量sa0.n(n+1)/2-1中。为了便于访问对称矩阵A中的元素,我们必需在aij和sak 之间找一个对应关系。 假设ij,那么ai j在下三角形中。 ai j之前的i行从第0行到第i-1行一共有1+2+i=i(i+1)/2个元素,在第i行上, ai j之前恰有j个元素即ai0,ai1,ai2,aij-1,因此有: k=i*(i+1)/2+j 0kn(n+1)/2 假设ij,那么aij是在上三角矩阵中。由于aij=aji,所以只需交换上述对应关系式中的i和j即可得到: k=j*(j+1)/2+i 0 kn(n+1)/2 令 I=max(i,j), J=min(i,j),那么k和 i, j的对应关系可一致为: k=I*(I+1)/2+J 0 kn(n+1)/2 因此,aij的地址可用以下式计算: LOC(aij)=LOC(sak) =LOC(sa0)+k*d=LOC(sa0+I*(I+1)/2+J*d 有了上述的下标交换关系,对于恣意给定一组下标(i,j),均可在sak中找到矩阵元素aij,反之,对一切的k=0,1,2,n(n-1)/2-1,都能确定sak中的元素在矩阵中的位置(i,j)。由此,称san(n+1)/2为阶对称矩阵A的紧缩存储,见以下图:k=0 1 2 3 n(n-1)/2 n(n+1)/2-1例如a21和a12均存储在 sa4中,这是由于 k=I*(I+1)/2+J=2*(2+1)/2+1=4a00a10a11a20an-1 0 an-1,n-12、三角矩阵 以主对角线划分,三角矩阵有上三角和下三角两种。上三角矩阵如下图,它的下三角不包括主对角线中的元素均为常数。下三角矩阵正好相反,它的主对角线上方均为常数,如下图。在大多数情况下,三角矩阵常数为零。 a00 a01 a 0 n-1 a00 c c c a11 a 1 n-1 a10 a11 c . . c c a n-1 n-1 an-1 0 an-1 1 an-1 n-1 (a)上三角矩阵 (b)下三角矩阵 图5.2 三角矩阵 三角矩阵中的反复元素c可共享一个存储空间,其他的元素正好有n(n+1)/2个,因此,三角矩阵可紧缩存储到向量sa0.n(n+1)/2中,其中c存放在向量的最后一个分量中, 上三角矩阵中,主对角线之上的第p行(0pjk= 下三角矩阵的存储和对称矩阵类似,sak和aij对应关系是: i(i+1)/2+j ij n(n+1)/2 ij 3、对角矩阵 对角矩阵中,一切的非零元素集中在以主对角线为了中心的带状区域中,即除了主对角线和主对角线相邻两侧的假设干条对角线上的元素之外,其他元素皆为零。以下图给出了一个三对角矩阵, a00 a01 a10 a11 a12 a21 a22 a23 . . . 图5.3 对角矩阵 an-2 n-3 an-2 n-2 an-2 n-1 an-1 n-2 an-1 n-1k=非零元素仅出如今主对角(aii,0in-1上,紧邻主对角线上面的那条对角线上(aii+1,0in-2)和紧邻主对角线下面的那条对角线上(ai+1 i,0in-2)。显然,当i-j1时,元素aij=0。由此可知,一个k对角矩阵(k为奇数)A是满足下述条件的矩阵:假设i-j(k-1)/2 ,那么元素 aij=0。 对角矩阵可按行优先顺序或对角线的顺序,将其紧缩存储到一个向量中,并且也能找到每个非零元素和向量下标的对应关系。 在三对角矩阵里附满足条件i=0,j=0、1,或i=n-1j=n-2、n-1或1in-1,j=i-1、i、i+1的元素aij外,其他元素都是零。 对这种矩阵,我们也可按行优序为主序来存储。除第0行和第n-1行是2个元素外,每行的非零元素都要是3个,因此,需存储的元素个数为3n-2。a00a01 a10a11a12a21 a n-1 n-2a n-1 n-1K=0 1 2 3 4 5 3n-2 3n-1 数组sa中的元素sak与三对角带状矩阵中的元素aij存在一一对应关系,在aij之前有i行,共有3*i-1个非零元素,在第i行,有j-i+1个非零元素,这样,非零元素aij的地址为: LOC(i,j)=LOC(0,0)+3*i-1+(j-i+1)*d =LOC(0,0)+(2i+j)*d 上例中,a34对应着sa10。 k=2*i+j=2*3+4=10 a21对应着sa5 k=2*2+1=5由此,我们称sa0.3*n-2是阶三对角带状矩阵A的紧缩存储表示。上述的各种特殊矩阵,其非零元素的分布都是有规律的,因此总能找到一种方法将它们紧缩存储到一个向量中,并且普通都能找到矩阵中的元素与该向量的对应关系,经过这个关系,仍能对矩阵的元素进展随机存取。 5.3.2 稀疏矩阵。随机稀疏矩阵的紧缩存储方法随机稀疏矩阵的紧缩存储方法:一、三元一、三元组顺序表序表二、行二、行逻辑联接的接的顺序表序表三、三、 十字十字链表表 #define MAXSIZE 12500 typedef struct int i, j; /该非零元的行下标和列下标 ElemType e; / 该非零元的值 Triple; / 三元组类型一、三元组顺序表一、三元组顺序表typedef union Triple dataMAXSIZE + 1; int mu, nu, tu; TSMatrix; / 稀疏矩稀疏矩阵类阵类型型如何求转置矩阵?如何求转置矩阵?用常规的二维数组表示时的算法 其其时间时间复复杂杂度度为为: O(munu) for (col=1; col=nu; +col) for (row=1; row=mu; +row) Tcolrow = Mrowcol;用“三元组表示时如何实现?1 2 141 5 -52 2 -73 1 363 4 282 1 145 1 -52 2 -71 3 364 3 28 首先应该确定每一行的第一个非零元在三元组中的位置。 cpot1 = 1; for (col=2; col=M.nu; +col) cpotcol = cpotcol-1 + numcol-1;Status FastTransposeSMatrix(TSMatrix M, TSMatrix &T) T.mu = M.nu; T.nu = M.mu; T.tu = M.tu; if (T.tu) for (col=1; col=M.nu; +col) numcol = 0; for (t=1; t=M.tu; +t) +numM.datat.j; cpot1 = 1; for (col=2; col=M.nu; +col) cpotcol = cpotcol-1 + numcol-1; for (p=1; p=M.tu; +p) / if return OK; / FastTransposeSMatrix 转置矩阵元素Col = M.datap.j;q = cpotcol;T.dataq.i = M.datap.j;T.dataq.j = M.datap.i;T.dataq.e = M.datap.e;+cpotcol 分析算法FastTransposeSMatrix的时间复杂度:时间复复杂度度为: O(M.nu+M.tu): O(M.nu+M.tu)for (col=1; col=M.nu; +col) for (t=1; t=M.tu; +t) for (col=2; col=M.nu; +col) for (p=1; p=M.tu; +p) 三元组顺序表又称有序的双下标法,它的特点是,非零元在表中按行序有序存储,因此便于进展依行顺序处置的矩阵运算。然而,假设需随机存取某一行中的非零元,那么需从头开场进展查找。二、行逻辑联接的顺序表二、行逻辑联接的顺序表 #define MAXMN 500 typedef struct Triple dataMAXSIZE + 1; int rposMAXMN + 1; int mu, nu, tu; RLSMatrix; / 行行逻辑链逻辑链接接顺顺序表序表类类型型 修正前述的稀疏矩阵的构造定义,添加一个数据成员rpos,其值在稀疏矩阵的初始化函数中确定。例如:给定一组下标,求矩阵的元素值ElemType value(RLSMatrix M, int r, int c) p = M.rposr; while (M.datap.i=r &M.datap.j c) p+; if (M.datap.i=r & M.datap.j=c) return M.datap.e; else return 0; / value矩阵乘法的精典算法矩阵乘法的精典算法: for (i=1; i=m1; +i) for (j=1; j=n2; +j) Qij = 0; for (k=1; k=n1; +k) Qij += Mik * Nkj; 其其时间复复杂度度为: O(m1n2n1) Q初始化; if Q是非零矩阵 / 逐行求积 for (arow=1; arow=M.mu; +arow) / 处置M的每一行 ctemp = 0; / 累加器清零 计算Q中第arow行的积并存入ctemp 中; 将ctemp 中非零元紧缩存储到Q.data; / for arow / if 两个稀疏矩阵相乘两个稀疏矩阵相乘Q MN 的过程可大致描画如下:的过程可大致描画如下: Status MultSMatrix (RLSMatrix M, RLSMatrix N, RLSMatrix &Q) if (M.nu != N.mu) return ERROR; Q.mu = M.mu; Q.nu = N.nu; Q.tu = 0; if (M.tu*N.tu != 0) / Q是非零矩阵是非零矩阵 for (arow=1; arow=M.mu; +arow) / 处置处置M的每一行的每一行 / for arow / if return OK; / MultSMatrix ctemp = 0; / 当前行各元素累加器清零 Q.rposarow = Q.tu+1; for (p=M.rposarow; pM.rposarow+1;+p) /对当前行中每一个非零元 brow=M.datap.j; if (brow N.nu ) t = N.rposbrow+1; else t = N.tu+1 for (q=N.rposbrow; q t; +q) ccol = N.dataq.j; / 乘积元素在Q中列号 ctempccol += M.datap.e * N.dataq.e; / for q / 求得Q中第crow( =arow)行的非零元 for (ccol=1; ccol MAXSIZE) return ERROR; Q.dataQ.tu = arow, ccol, ctempccol; / if处置 的每一行M分析上述算法的时间复杂度分析上述算法的时间复杂度累加器ctemp初始化的时间复杂度为(M.muN.nu),求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,相乘算法的时间复杂度就是 (mp(1+nMN) ,当M0.05 和N0.05及 n 1000时,相乘算法的时间复杂度就相当于 (mp)。三、三、 十字链表十字链表3 0 0 50 -1 0 02 0 0 01 1 31 4 52 2-13 1 2 5.4 广广义义表的表的类类型定型定义义ADT Glist 数据数据对对象:象:Dei | i=1,2,.,n; n0; eiAtomSet 或或 eiGList, AtomSet为为某个数据某个数据对对象象 数据关系:数据关系: LR| ei-1 ,eiD, 2in ADT Glist根本操作根本操作:广义表是递归定义的线性构造, LS = ( 1, 2, , n )其中:i 或为原子 或为广义表例如例如: A = ( ) F = (d, (e) D = (a,(b,c), F) C = (A, D, F) B = (a, B) = (a, (a, (a, , ) ) )广义表是一个多层次的线性构造例如:例如:D=(E, F)其中: E=(a, (b, c) F=(d, (e)DEFa( ) d( )bce广广义表表 LS = ( 1, 2, , n )的构的构造特点造特点:1) 广义表中的数据元素有相对次序;2) 广义表的长度定义为最外层包含元素个数;3) 广义表的深度定义为所含括弧的重数; 留意:“原子的深度为 0 “空表的深度为 1 4) 广义表可以共享;5) 广义表可以是一个递归的表。 递归表的深度是无穷值,长度是有限值。6) 任何一个非空广义表 LS = ( 1, 2, , n) 均可分解为 表头 Head(LS) = 1 和 表尾 Tail(LS) = ( 2, , n) 两部分。例如例如: D = ( E, F ) = (a, (b, c),F )Head( D ) = E Tail( D ) = ( F )Head( E ) = a Tail( E ) = ( ( b, c) )Head( ( b, c) ) = ( b, c) Tail( ( b, c) ) = ( )Head( ( b, c) ) = b Tail( ( b, c) ) = ( c )Head( ( c ) ) = c Tail( ( c ) ) = ( ) 构造的创建和销毁 InitGList(&L); DestroyGList(&L); CreateGList(&L, S); CopyGList(&T, L);根根本本操操作作 形状函数形状函数 GListLength(L); GListDepth(L); GListEmpty(L); GetHead(L); GetTail(L); 插入和插入和删删除操作除操作 InsertFirst_GL(&L, e); DeleteFirst_GL(&L, &e); 遍遍历历 Traverse_GL(L, Visit();5.5 广广义义表的表示方法表的表示方法通常采用头、尾指针的链表构造表表结点点: :原子原子结点:点:tag=1 hp tptag=0 data1) 表头、表尾分析法:构造存构造存储构造的两种分析方法构造的两种分析方法: :假假设表表头为原子,那么原子,那么为空表空表 ls=NIL非空表非空表 lstag=1 指向表头的指针指向表尾的指针tag=0 data否那么,依次否那么,依次类推。推。L = ( a, ( x, y ), ( ( x ) ) )a ( x, y ) ( ) 1 LL = ( )0 a 1 1 1 1 1 0 x ( )x2) 子表分析法:假假设子表子表为原子,那么原子,那么为空表空表 ls=NIL非空表非空表 1 指向子表1 的指针tag=0 data否那么,依次否那么,依次类推。推。 1 指向子表2 的指针 1 指向子表n 的指针ls 例如例如: a (x, y) (x) LS=( a, (x,y), (x) )ls5.6 广广义义表操作的表操作的递归递归函数函数递归函数函数 一个含直接或一个含直接或间接接调用本函数用本函数语句的函数被称之句的函数被称之为递归函数,它函数,它必需必需满足以下两个条件:足以下两个条件:1)在每一次在每一次调调用本人用本人时时,必需是,必需是(在某在某 种意种意义义上上)更接近于解更接近于解;2)必需有一个必需有一个终终止止处处置或置或计计算的准那么。算的准那么。例如例如: : 梵塔的梵塔的递归函数函数void hanoi (int n, char x, char y, char z) if (n=1) move(x, 1, z); else hanoi(n-1, x, z, y); move(x, n, z); hanoi(n-1, y, x, z); 二叉树的遍历二叉树的遍历 void PreOrderTraverse( BiTree T,void (Visit)(BiTree P) if (T) Visit(T-data); (PreOrderTraverse(T-lchild, Visit); (PreOrderTraverse(T-rchild, Visit); / PreOrderTraverse一、分治法一、分治法 (Divide and Conquer) (又称分割求解法又称分割求解法)如何如何设计递归函数?函数?二、后置二、后置递归法法(Postponing the work)三、回溯法三、回溯法(Backtracking) 对于一个输入规模为 n 的函数或问题,用某种方法把输入分割成 k(1ptr.tp) dep = GlistDepth(pp-ptr.hp); if (dep max) max = dep; return max + 1; / GlistDepthif (!L) return 1; if (L-tag = ATOM) return 0; 1 1 1 L for (max=0, pp=L; pp; pp=pp-ptr.tp) dep = GlistDepth(pp-ptr.hp); if (dep max) max = dep; 例如例如:pppp-ptr.hppppppp-ptr.hppp-ptr.hp例二例二 复制广复制广义表表新的广新的广义表由新的表表由新的表头和表尾构成。和表尾构成。可以直接求解的两种简单情况为: 空表复制求得的新表自然也是空表; 原子结点可以直接复制求得。 将广义表分解成表头和表尾两部分,分别(递归)复制求得新的表头和表尾,假假设 ls= NIL 那么那么 newls = NIL否那么否那么 构造构造结点点 newls, 由由 表表头ls-ptr.hp 复制得复制得 newhp 由由 表尾表尾 ls-ptr.tp 复制得复制得 newtp 并使并使 newls-ptr.hp = newhp, newls-ptr.tp = newtp复制求广复制求广义表的算法描画如下表的算法描画如下:Status CopyGList(Glist &T, Glist L) if (!L) T = NULL; / 复制空表复制空表 else if ( !(T = (Glist)malloc(sizeof(GLNode) ) exit(OVERFLOW); / 建表结点建表结点 T-tag = L-tag; if (L-tag = ATOM) T-atom = L-atom; / 复制单原子结点复制单原子结点 else / else return OK; / CopyGList分别复制表头和表尾分别复制表头和表尾CopyGList(T-ptr.hp, L-ptr.hp); / 复制求得表头复制求得表头T-ptr.hp的一个副本的一个副本L-ptr.hpCopyGList(T-ptr.tp, L-ptr.tp); / 复制求得表尾复制求得表尾T-ptr.tp 的一个副本的一个副本L-ptr.tp语句语句 CopyGList(T-ptr.hp, L-ptr.hp);等价于等价于 CopyGList(newhp, L-ptr.tp); T-ptr.hp = newhp;例三例三 创建广义表的存储构造创建广义表的存储构造 对应广义表的不同定义方法相应地有不同的创建存储构造的算法。 假设以字符串 S = (1, 2, , n ) 的方式定义广义表 L,建立相应的存储构造。 由于S中的每个子串i定义 L 的一个子表,从而产生 n 个子问题,即分别由这 n个子串 (递归)建立 n 个子表,再组合成一个广义表。 可以直接求解的两种简单情况为:由串( )建立的广义表是空表;由单字符建立的子表只是一个原子结点。如何由子表如何由子表组合成一个广合成一个广义表?表? 首先分析广首先分析广义义表和子表在存表和子表在存储储构造中构造中的关系。的关系。先看第一个子表和广先看第一个子表和广义表的关系表的关系: 1 L指向广义表指向广义表的头指针的头指针指向第一个指向第一个子表的头指针子表的头指针再看相再看相邻两个子表之两个子表之间的关系的关系: 1 1 指向第指向第i+1个个子表的头指针子表的头指针指向第指向第i个个子表的头指针子表的头指针可可见,两者之,两者之间经过表表结点相点相链接。接。假假设 S = ( ) 那么那么 L = NIL;否那么,构造第一个表否那么,构造第一个表结点点 *L, 并从串并从串S中分解出第一个子串中分解出第一个子串1,对应创建第一个子广建第一个子广义表表 L-ptr.hp; 假假设剩余串非空,那么构造第二个表剩余串非空,那么构造第二个表结点点 L-ptr.tp,并从串,并从串S中分解出第二中分解出第二个子串个子串 2,对应创建第二个子广建第二个子广义表表 ; 依次依次类推,直至剩余串推,直至剩余串为空串止。空串止。void CreateGList(Glist &L, String S) if (空串空串) L = NULL; / 创创建空表建空表 else L=(Glist) malloc(sizeof(GLNode); L-tag=List; p=L; sub=SubString(S,2,StrLength(S)-1); /脱去串脱去串S的外的外层层括弧括弧 / else 由由sub中所含中所含n个子串建立个子串建立n个子表个子表;do sever(sub, hsub); / 分分别别出子表串出子表串hsub=i if (!StrEmpty(sub) p-ptr.tp=(Glist)malloc(sizeof(GLNode); / 建下一个子表的表建下一个子表的表结结点点*(p-ptr.tp) p=p-ptr.tp; while (!StrEmpty(sub);p-ptr.tp = NULL; / 表尾表尾为为空表空表创建由串建由串hsub定定义的广的广义表表p-ptr.hp;if (StrLength(hsub)=1) p-ptr.hp=(GList)malloc(sizeof(GLNode); p-ptr.hp-tag=ATOM; p-ptr.hp-atom=hsub; / 创创建建单单原子原子结结点点else CreateGList(p-ptr.hp, hsub); /递归递归建广建广义义表表 后置后置递归的的设计思想思想为: 递归的终结形状是,当前的问题可以直接求解,对原问题而言,那么是已走到了求解的最后一步。链表是可以如此求解的一个典型例子。例如:编写“删除单链表中一切值为x 的数据元素的算法。1) 单链表是一种顺序构造,必需从第一个结点起,逐个检查每个结点的数据元素;分析分析:2) 从另一角度看,链表又是一个递归构造,假设 L 是线性链表 (a1, a2, , an) 的头指针,那么 L-next是线性链表 (a2, , an)的头指针。 a1 a2 a3 an L例如例如: a1 a2 a3 an L a1 a2 a3 an L知以下链表1) “a1=x,那么 L 仍为删除 x 后的链表头指针2) “a1x,那么余下问题是思索以 L-next 为头指针的链表 a1 L-nextL-next=p-nextp=L-nextvoid delete(LinkList &L, ElemType x) / 删删除以除以L为头为头指指针针的的带头结带头结点的点的单链单链表中表中 / 一切一切值为值为x的数据元素的数据元素 if (L-next) if (L-next-data=x) p=L-next; L-next=p-next; free(p); delete(L, x); else delete(L-next, x); / delete删除广除广义表中一切元素表中一切元素为x x的原子的原子结点点分析分析: : 比比较广广义表和表和线性表的构造特点:性表的构造特点:类似似处:都是:都是链表构造。表构造。不同不同处:1)1)广广义表的数据元素能表的数据元素能够还是个是个 广广义表;表; 2) 2)删除除时,不,不仅要要删除原子除原子结点,点, 还需求需求删除相除相应的表的表结点。点。void Delete_GL(Glist&L, AtomType x) /删删除广除广义义表表L中一切中一切值为值为x的原子的原子结结点点 if (L) head = L-ptr.hp; / 调查调查第一个子表第一个子表 if (head-tag = Atom) & (head-atom = x) / 删删除原子除原子项项 x的情况的情况 else / 第一第一项项没有被没有被删删除的情况除的情况 / Delete_GL p=L; L = L-ptr.tp; / 修正指针free(head); / 释放原子结点free(p); / 释放表结点Delete_GL(L, x); / 递归处置剩余表项 1 L0 x 1 pL headif (head-tag = LIST) /该项为该项为广广义义表表 Delete_GL(head, x);Delete_GL(L-ptr.tp, x); / 递归处递归处置剩余表置剩余表项项 1 L0 a 1 1 headL-ptr.tp回溯法是一种回溯法是一种“穷举方法。其根本思想方法。其根本思想为: 假设问题的解为 n 元组 (x1, x2, , xn),其中 xi 取值于集合 Si。 n 元组的子组 (x1, x2, , xi) (in)的一的一个合法个合法规规划划 / 时时,输输出之。出之。 if (in) 输输出棋出棋盘盘的当前的当前规规划划; else for (j=1; jn) else while ( ! Empty(Si) 从从 Si 中取中取 xi 的一个的一个值值 viSi; if (x1, x2, , xi) 满满足足约约束条件束条件 B( i+1, n); / 继续继续求下一个部分解求下一个部分解 从从 Si 中中删删除除值值 vi; / B综合几点:合几点:1. 对于含有于含有递归特性的特性的问题,最好,最好设计递归方式的算法。但也不要方式的算法。但也不要单纯追求方追求方式,式,应在算法在算法设计的分析的分析过程中程中“就事就事论事。例如,在利用分割求解事。例如,在利用分割求解设计算算法法时,子,子问题和原和原问题的性的性质一一样;或;或者,者,问题的当前一步的当前一步处理之后,余下的理之后,余下的问题和原和原问题性性质一一样,那么自然,那么自然导致致递归求解。求解。2. 实现递归实现递归函数,目前必需利用函数,目前必需利用“栈栈。一个。一个递归递归函数必定能改写函数必定能改写为为利利用用栈实现栈实现的非的非递归递归函数;反之,一函数;反之,一个用个用栈实现栈实现的非的非递归递归函数可以改写函数可以改写为递归为递归函数。需求留意的是函数。需求留意的是递归递归函函数数递归层递归层次的深度决次的深度决议议所需存所需存储储量量的大小。的大小。3. 分析递归算法的工具是递归树,从分析递归算法的工具是递归树,从递归树上可以得到递归函数的各种相递归树上可以得到递归函数的各种相关信息。例如:递归树的深度即为递关信息。例如:递归树的深度即为递归函数的递归深度;递归树上的结点归函数的递归深度;递归树上的结点数目恰为函数中的主要操作反复进展数目恰为函数中的主要操作反复进展的次数;假设递归树蜕化为单支树或的次数;假设递归树蜕化为单支树或者递归树中含有很多一样的结点,那者递归树中含有很多一样的结点,那么阐明该递归函数不适用。么阐明该递归函数不适用。 例如: n=3的梵塔算法中主要操作move的执行次数可以利用以下递归树进展分析:move(3, a, b, c)move(2, a, c, b)move(2, b, a, c)move(1, a, b, c)move(1, c, a, b)move(1, b, c, a)move(1, a, b, c)上上图递归树的中序序列即的中序序列即为圆盘的挪的挪动操作序列。操作序列。又如又如: 求求n!的的递归函数的函数的递归树已退化已退化为一个一个单枝枝树;而而计算斐波那契算斐波那契递归函数的函数的递归树中中有很多反复出有很多反复出现的的结点。点。nn-110。F5F4F3F3F2F2F1F1F0F1F0。4. 递归函数中的尾递归容易消除。递归函数中的尾递归容易消除。例如:先序遍历二叉树可以改写为:例如:先序遍历二叉树可以改写为: void PreOrderTraverse( BiTree T) While (T) Visit(T-data); PreOrderTraverse(T-lchild); T = T-rchild; / PreOrderTraversevoid delete(LinkList &L, ElemType x) / L为为无无头结头结点的点的单链单链表的表的头头指指针针 if (L) if (L-data=x) p=L; L=L-next; free(p); delete(L, x); else delete(L-next, x); 又如又如:void delete(LinkList &L, ElemType x) / L为带头结为带头结点的点的单链单链表的表的头头指指针针 p=L-next; pre=L; while (p) if (p-data=x) pre-next=p-next; free(p); p=pre-next; else pre=p; p=p-next; 可可改改写写为 5. 可以用递归方程来表述递归函数的可以用递归方程来表述递归函数的 时间性能。时间性能。 例如例如: 假设解假设解n个圆盘的梵塔的执行个圆盘的梵塔的执行 时间为时间为T(n) 那么递归方程为:那么递归方程为: T(n) = 2T(n-1) + C, 初始条件为:初始条件为: T(0) = 01. 1. 了了解解数数组组的的两两种种存存储储表表示示方方法法,并并掌掌握握数数组组在在以以行行为为主主的的存存储储构构造造中中的地址计算方法。的地址计算方法。2. 2. 掌掌握握对对特特殊殊矩矩阵阵进进展展紧紧缩缩存存储储时时的下标变换公式。的下标变换公式。3. 3. 了了解解稀稀疏疏矩矩阵阵的的两两类类紧紧缩缩存存储储方方法法的的特特点点和和适适用用范范围围,领领会会以以三三元元组组表表示示稀稀疏疏矩矩阵阵时时进进展展矩矩阵阵运运算算采采用用的的处置方法。处置方法。4. 4. 掌握广义表的构造特点及其存储掌握广义表的构造特点及其存储表示方法,读者可根据本人的习惯表示方法,读者可根据本人的习惯熟练掌握恣意一种构造的链表,学熟练掌握恣意一种构造的链表,学会对非空广义表进展分解的两种分会对非空广义表进展分解的两种分析方法:即可将一个非空广义表分析方法:即可将一个非空广义表分解为表头和表尾两部分或者分解为解为表头和表尾两部分或者分解为n n个子表。个子表。 5. 5. 学习利用分治法的算法设计思学习利用分治法的算法设计思想编制递归算法的方法。想编制递归算法的方法。
网站客服QQ:2055934822
金锄头文库版权所有
经营许可证:蜀ICP备13022795号 | 川公网安备 51140202000112号