收藏 分销(赏)

数据结构和算法部分经典例子.doc

上传人:a199****6536 文档编号:2615185 上传时间:2024-06-03 格式:DOC 页数:35 大小:88.54KB
下载 相关 举报
数据结构和算法部分经典例子.doc_第1页
第1页 / 共35页
数据结构和算法部分经典例子.doc_第2页
第2页 / 共35页
数据结构和算法部分经典例子.doc_第3页
第3页 / 共35页
数据结构和算法部分经典例子.doc_第4页
第4页 / 共35页
数据结构和算法部分经典例子.doc_第5页
第5页 / 共35页
点击查看更多>>
资源描述

1、数据结构和算法部分经典例子 一、迭代法 迭代法是用于求方程或方程组近似根的一种常用的算法设计方法.设方程为f(x)=0,用某种数学方法导出等价的形式x=g(x),然后按以下步骤执行: (1) 选一个方程的近似根,赋给变量x0; (2) 将x0的值保存于变量x1,然后计算g(x1),并将结果存于变量x0; (3) 当x0与x1的差的绝对值还小于指定的精度要求时,重复步骤(2)的计算. 若方程有根,并且用上述方法计算出来的近似根序列收敛,则按上述方法求得的x0就认为是方程的根。上述算法用C程序的形式表示为: 【算法】迭代法求方程的根 x0=初始近似根; do x1=x0; x0=g(x1); /*

2、按特定的方程计算新的近似根*/ while ( fabs(x0x1)Epsilon); printf(“方程的近似根是fn”,x0); 迭代算法也常用于求方程组的根,令 X=(x0,x1,xn-1) 设方程组为: xi=gi(X) (I=0,1,,n1) 则求方程组根的迭代算法可描述如下: 【算法】迭代法求方程组的根 for (i=0;in;i+) xi=初始近似根; do for (i=0;in;i+) yi=xi; for (i=0;in;i+) xi=gi(X); for (delta=0.0,i=0;in;i+) if (fabs(yixi)delta) delta=fabs(yixi

3、); while (deltaEpsilon); for (i=0;in;i+) printf(“变量xd的近似根是 f”,I,xi); printf(“n); 具体使用迭代法求根时应注意以下两种可能发生的情况: (1) 如果方程无解,算法求出的近似根序列就不会收敛,迭代过程会变成死循环,因此在使用迭代算法前应先考察方程是否有解,并在程序中对迭代的次数给予限制; (2) 方程虽然有解,但迭代公式选择不当,或迭代的初始近似根选择不合理,也会导致迭代失败。 二、穷举搜索法 本文为互联网收集,请勿用作商业用途穷举搜索法是对可能是解的众多候选解按某种顺序进行逐一枚举和检验,并从众找出那些符合要求的候选

4、解作为问题的解. 【问题】 将A、B、C、D、E、F这六个变量排成如图所示的三角形,这六个变量分别取1,6上的整数,且均不相同。求使三角形三条边上的变量之和相等的全部解。如图就是一个解。 程序引入变量a、b、c、d、e、f,并让它们分别顺序取1至6的证书,在它们互不相同的条件下,测试由它们排成的如图所示的三角形三条边上的变量之和是否相等,如相等即为一种满足要求的排列,把它们输出。当这些变量取尽所有的组合后,程序就可得到全部可能的解.细节见下面的程序. 【程序1】 # include stdio.h void main() int a,b,c,d,e,f; for (a=1;a=6;a+) fo

5、r (b=1;b=6;b+) if (b=a) continue; for (c=1;c=6;c+) if (c=a)|(c=b) continue; for (d=1;d=6;d+) if (d=a)(d=b)|(d=c) continue; for (e=1;e=6;e+) if (e=a)|(e=b)|(e=c)(e=d) continue; f=21-(a+b+c+d+e); if (a+b+c=c+d+e))(a+b+c=e+f+a) printf(“6d,a); printf(“4d%4d”,b,f); printf(“2d4d%4d,c,d,e); scanf(“%*c”); 按

6、穷举法编写的程序通常不能适应变化的情况.如问题改成有9个变量排成三角形,每条边有4个变量的情况,程序的循环重数就要相应改变. 对一组数穷尽所有排列,还有更直接的方法。将一个排列看作一个长整数,则所有排列对应着一组整数。将这组整数按从小到大的顺序排列排成一个整数,从对应最小的整数开始.按数列的递增顺序逐一列举每个排列对应的每个整数,这能更有效地完成排列的穷举。从一个排列找出对应数列的下一个排列可在当前排列的基础上作部分调整来实现。倘若当前排列为1,2,4,6,5,3,并令其对应的长整数为124653。要寻找比长整数124653更大的排列,可从该排列的最后一个数字顺序向前逐位考察,当发现排列中的某

7、个数字比它前一个数字大时,如本例中的6比它的前一位数字4大,这说明还有对应更大整数的排列。但为了顺序从小到大列举出所有的排列,不能立即调整得太大,如本例中将数字6与数字4交换得到的排列126453就不是排列124653的下一个排列.为了得到排列124653的下一个排列,应从已经考察过的那部分数字中选出比数字大,但又是它们中最小的那一个数字,比如数字5,与数字4交换。该数字也是从后向前考察过程中第一个比4大的数字.5与4交换后,得到排列125643。在前面数字1,2,5固定的情况下,还应选择对应最小整数的那个排列,为此还需将后面那部分数字的排列顺序颠倒,如将数字6,4,3的排列顺序颠倒,得到排列

8、1,2,5,3,4,6,这才是排列1,2,4,6,5,3的下一个排列。按以上想法编写的程序如下。 个人收集整理,勿做商业用途2 数据结构(ZT) 【程序2】 # include stdio.h # define SIDE_N 3 # define LENGTH 3 define VARIABLES 6 int A,B,C,D,E,F; int *pt=A,B,C,&D,E,&F; int *sideSIDE_NLENGTH=A,B,C,&C,D,&E,E,&F,&A; int side_totalSIDE_N; main int i,j,t,equal; for (j=0;jVARIABLES

9、;j+) ptj=j+1; while(1) for (i=0;iSIDE_N;i+) for (t=j=0;jLENGTH;j+) t+=sideij; side_totali=t; for (equal=1,i=0;equal&i0;j-) if (*ptjptj-1) break; if (j=0) break; for (i=VARIABLES1;i=j;i-) if (pti*pti-1) break; t=ptj-1;* ptj-1 = pti; pti=t; for (i=VARIABLES1;ij;i-,j+) t=*ptj; *ptj =* pti; *pti=t; 从上述问

10、题解决的方法中,最重要的因素就是确定某种方法来确定所有的候选解。下面再用一个示例来加以说明。 【问题】 背包问题 问题描述:有不同价值、不同重量的物品n件,求从这n件物品中选取一部分物品的选择方案,使选中物品的总重量不超过指定的限制重量,但选中物品的价值之和最大。 设n个物品的重量和价值分别存储于数组w 和v 中,限制重量为tw.考虑一个n元组(x0,x1,xn-1),其中xi=0 表示第i个物品没有选取,而xi=1则表示第i个物品被选取。显然这个n元组等价于一个选择方案。用枚举法解决背包问题,需要枚举所有的选取方案,而根据上述方法,我们只要枚举所有的n元组,就可以得到问题的解。 显然,每个分

11、量取值为0或1的n元组的个数共为2n个。而每个n元组其实对应了一个长度为n的二进制数,且这些二进制数的取值范围为02n1。因此,如果把02n1分别转化为相应的二进制数,则可以得到我们所需要的2n个n元组。 【算法】 maxv=0; for (i=0;i2n;i+) B0。.n-1=0; 把i转化为二进制数,存储于数组B中; temp_w=0; temp_v=0; for (j=0;jmaxv)) maxv=temp_v; 保存该B数组; 三、递推法 递推法是利用问题本身所具有的一种递推关系求问题解的一种方法。设要求问题规模为N的解,当N=1时,解或为已知,或能非常方便地得到解。能采用递推法构造

12、算法的问题有重要的递推性质,即当得到问题规模为i1的解后,由问题的递推性质,能从已求得的规模为1,2,i-1的一系列解,构造出问题规模为I的解。这样,程序可从i=0或i=1出发,重复地,由已知至i1规模的解,通过递推,获得规模为i的解,直至得到规模为N的解。 【问题】 阶乘计算 问题描述:编写程序,对给定的n(n100),计算并输出k的阶乘k!(k=1,2,,n)的全部有效数字. 由于要求的整数可能大大超出一般整数的位数,程序用一维数组存储长整数,存储长整数数组的每个元素只存储长整数的一位数字。如有m位成整数N用数组a 存储: N=am10m1+am110m-2+ +a2101+a1100 并

13、用a0存储长整数N的位数m,即a0=m。按上述约定,数组的每个元素存储k的阶乘k!的一位数字,并从低位到高位依次存于数组的第二个元素、第三个元素.例如,5!=120,在数组中的存储形式为: 个人收集整理,勿做商业用途3 数据结构(ZT) 3 0 2 1 首元素3表示长整数是一个3位数,接着是低位到高位依次是0、2、1,表示成整数120。 计算阶乘k!可采用对已求得的阶乘(k1)!连续累加k1次后求得.例如,已知4!=24,计算5!,可对原来的24累加4次24后得到120。细节见以下程序。 include stdio.h include malloc.h # define MAXN 1000 v

14、oid pnext(int a ,int k) int *b,m=a0,i,j,r,carry; b=(int ) malloc(sizeof(int) (m+1); for ( i=1;i=m;i+) bi=ai; for ( j=1;j=k;j+) for ( carry=0,i=1;i0;i) printf(“%d”,ai); printf(“nn”); void main() int aMAXN,n,k; printf(“Enter the number n: “); scanf(“%d,n); a0=1; a1=1; write(a,1); for (k=2;k1时)。 写成递归函数

15、有: int fib(int n) if (n=0) return 0; if (n=1) return 1; if (n1) return fib(n-1)+fib(n2); 递归算法的执行过程分递推和回归两个阶段。在递推阶段,把较复杂的问题(规模为n)的求解推到比原问题简单一些的问题(规模小于n)的求解.例如上例中,求解fib(n),把它推到求解fib(n1)和fib(n-2).也就是说,为计算fib(n),必须先计算fib(n-1)和fib(n-2),而计算fib(n-1)和fib(n2),又必须先计算fib(n-3)和fib(n4)。依次类推,直至计算fib(1)和fib(0),分别能

16、立即得到结果1和0。在递推阶段,必须要有终止递归的情况。例如在函数fib中,当n为1和0的情况。 在回归阶段,当获得最简单情况的解后,逐级返回,依次得到稍复杂问题的解,例如得到fib(1)和fib(0)后,返回得到fib(2)的结果,在得到了fib(n-1)和fib(n2)的结果后,返回得到fib(n)的结果. 在编写递归函数时要注意,函数中的局部变量和参数知识局限于当前调用层,当递推进入“简单问题层时,原来层次上的参数和局部变量便被隐蔽起来.在一系列“简单问题层,它们各有自己的参数和局部变量。 由于递归引起一系列的函数调用,并且可能会有一系列的重复计算,递归算法的执行效率相对较低.当某个递归

17、算法能较方便地转换成递推算法时,通常按递推算法编写程序。例如上例计算斐波那契数列的第n项的函数fib(n)应采用递推算法,即从斐波那契数列的前两项出发,逐次由前两项计算出下一项,直至计算出要求的第n项. 【问题】 组合问题 问题描述:找出从自然数1、2、n中任取r个数的所有组合。例如n=5,r=3的所有组合为: (1)5、4、3 (2)5、4、2 (3)5、4、1 文档为个人收集整理,来源于网络4 数据结构(ZT) (4)5、3、2 (5)5、3、1 (6)5、2、1 (7)4、3、2 (8)4、3、1 (9)4、2、1 (10)3、2、1 分析所列的10个组合,可以采用这样的递归思想来考虑求

18、组合函数的算法。设函数为void comb(int m,int k)为找出从自然数1、2、m中任取k个数的所有组合。当组合的第一个数字选定时,其后的数字是从余下的m-1个数中取k-1数的组合。这就将求m个数中取k个数的组合问题转化成求m1个数中取k1个数的组合问题。设函数引入工作数组a 存放求出的组合的数字,约定函数将确定的k个数字组合的第一个数字放在ak中,当一个组合求出后,才将a 中的一个组合输出.第一个数可以是m、m1、k,函数将确定组合的第一个数字放入数组后,有两种可能的选择,因还未去顶组合的其余元素,继续递归去确定;或因已确定了组合的全部元素,输出这个组合。细节见以下程序中的函数co

19、mb。 【程序】 # include stdio。h # define MAXN 100 int aMAXN; void comb(int m,int k) int i,j; for (i=m;i=k;i-) ak=i; if (k1) comb(i1,k-1); else for (j=a0;j0;j) printf(“4d”,aj); printf(“n); 本文为互联网收集,请勿用作商业用途void main() a0=3; comb(5,3); 【问题】 背包问题 问题描述:有不同价值、不同重量的物品n件,求从这n件物品中选取一部分物品的选择方案,使选中物品的总重量不超过指定的限制重量

20、,但选中物品的价值之和最大。 设n件物品的重量分别为w0、w1、wn1,物品的价值分别为v0、v1、vn-1。采用递归寻找物品的选择方案。设前面已有了多种选择的方案,并保留了其中总价值最大的方案于数组option ,该方案的总价值存于变量maxv。当前正在考察新方案,其物品选择情况保存于数组cop 。假定当前方案已考虑了前i1件物品,现在要考虑第i件物品;当前方案已包含的物品的重量之和为tw;至此,若其余物品都选择是可能的话,本方案能达到的总价值的期望值为tv.算法引入tv是当一旦当前方案的总价值的期望值也小于前面方案的总价值maxv时,继续考察当前方案变成无意义的工作,应终止当前方案,立即去

21、考察下一个方案.因为当方案的总价值不比maxv大时,该方案不会被再考察,这同时保证函数后找到的方案一定会比前面的方案更好. 对于第i件物品的选择考虑有两种可能: (1) 考虑物品i被选择,这种可能性仅当包含它不会超过方案总重量限制时才是可行的。选中后,继续递归去考虑其余物品的选择. (2) 考虑物品i不被选择,这种可能性仅当不包含物品i也有可能会找到价值更大的方案的情况。 按以上思想写出递归算法如下: try(物品i,当前选择已达到的重量和,本方案可能达到的总价值tv) /考虑物品i包含在当前方案中的可能性/ if(包含物品i是可以接受的) 将物品i包含在当前方案中; if (in1) try

22、(i+1,tw+物品i的重量,tv); else /又一个完整方案,因为它比前面的方案好,以它作为最佳方案/ 以当前方案作为临时最佳方案保存; 恢复物品i不包含状态; /*考虑物品i不包含在当前方案中的可能性*/ if (不包含物品i仅是可男考虑的) if (ij.因此,对于约束集D具有完备性的问题P,一旦检测断定某个j元组(x1,x2,xj)违反D中仅涉及x1,x2,xj的一个约束,就可以肯定,以(x1,x2,,xj)为前缀的任何n元组(x1,x2,xj,xj+1,,xn)都不会是问题P的解,因而就不必去搜索它们、检测它们。回溯法正是针对这类问题,利用这类问题的上述性质而提出来的比枚举法效率

23、更高的算法。 回溯法首先将问题P的n元组的状态空间E表示成一棵高为n的带权有序树T,把在E中求问题P的所有解转化为在T中搜索问题P的所有解。树T类似于检索树,它可以这样构造: 设Si中的元素可排成xi(1) ,xi(2) ,xi(mi1) ,|Si| =mi,i=1,2,,n。从根开始,让T的第I层的每一个结点都有mi个儿子.这mi个儿子到它们的双亲的边,按从左到右的次序,分别带权xi+1(1) ,xi+1(2) ,,xi+1(mi) ,i=0,1,2,n-1.照这种构造方式,E中的一个n元组(x1,x2,,xn)对应于T中的一个叶子结点,T的根到这个叶子结点的路径上依次的n条边的权分别为x1

24、,x2,xn,反之亦然。另外,对于任意的0in1,E中n元组(x1,x2,xn)的一个前缀I元组(x1,x2,xi)对应于T中的一个非叶子结点,T的根到这个非叶子结点的路径上依次的I条边的权分别为x1,x2,xi,反之亦然。特别,E中的任意一个n元组的空前缀(),对应于T的根。 因而,在E中寻找问题P的一个解等价于在T中搜索一个叶子结点,要求从T的根到该叶子结点的路径上依次的n条边相应带的n个权x1,x2,,xn满足约束集D的全部约束。在T中搜索所要求的叶子结点,很自然的一种方式是从根出发,按深度优先的策略逐步深入,即依次搜索满足约束条件的前缀1元组(x1i)、前缀2元组(x1,x2)、,前缀

25、I元组(x1,x2,xi),,直到i=n为止。 在回溯法中,上述引入的树被称为问题P的状态空间树;树T上任意一个结点被称为问题P的状态结点;树T上的任意一个叶子结点被称为问题P的一个解状态结点;树T上满足约束集D的全部约束的任意一个叶子结点被称为问题P的一个回答状态结点,它对应于问题P的一个解。 文档为个人收集整理,来源于网络7 回复:数据结构(ZT) 【问题】 组合问题 问题描述:找出从自然数1、2、n中任取r个数的所有组合。 例如n=5,r=3的所有组合为: (1)1、2、3 (2)1、2、4 (3)1、2、5 (4)1、3、4 (5)1、3、5 (6)1、4、5 (7)2、3、4 (8)

26、2、3、5 (9)2、4、5 (10)3、4、5 则该问题的状态空间为: E=(x1,x2,x3)xiS ,i=1,2,3 其中:S=1,2,3,4,5 约束集为: x1ai,后一个数字比前一个大; (2) ai-i=nr+1。 按回溯法的思想,找解过程可以叙述如下: 首先放弃组合数个数为r的条件,候选组合从只有一个数字1开始.因该候选解满足除问题规模之外的全部条件,扩大其规模,并使其满足上述条件(1),候选组合改为1,2。继续这一过程,得到候选组合1,2,3。该候选解满足包括问题规模在内的全部条件,因而是一个解。在该解的基础上,选下一个候选解,因a2上的3调整为4,以及以后调整为5都满足问题

27、的全部要求,得到解1,2,4和1,2,5。由于对5不能再作调整,就要从a2回溯到a1,这时,a1=2,可以调整为3,并向前试探,得到解1,3,4。重复上述向前试探和向后回溯,直至要从a0再回溯时,说明已经找完问题的全部解。按上述思想写成程序如下: 文档为个人收集整理,来源于网络8 回复:数据结构(ZT) 【程序】 # define MAXN 100 int aMAXN; void comb(int m,int r) int i,j; i=0; ai=1; do if (ai-i=mr+1 if (i=r1) for (j=0;jr;j+) printf(“4d”,aj); printf(“n”

28、); ai+; continue; else if (i=0) return; a-i+; while (1) main() comb(5,3); 【问题】 填字游戏 问题描述:在33个方格的方阵中要填入数字1到N(N10)内的某9个数字,每个方格填一个整数,似的所有相邻两个方格内的两个整数之和为质数。试求出所有满足这个要求的各种数字填法。 可用试探发找到问题的解,即从第一个方格开始,为当前方格寻找一个合理的整数填入,并在当前位置正确填入后,为下一方格寻找可填入的合理整数。如不能为当前方格找到一个合理的可填证书,就要回退到前一方格,调整前一方格的填入数。当第九个方格也填入合理的整数后,就找到了

29、一个解,将该解输出,并调整第九个的填入的整数,寻找下一个解。 为找到一个满足要求的9个数的填法,从还未填一个数开始,按某种顺序(如从小到大的顺序)每次在当前位置填入一个整数,然后检查当前填入的整数是否能满足要求.在满足要求的情况下,继续用同样的方法为下一方格填入整数.如果最近填入的整数不能满足要求,就改变填入的整数。如对当前方格试尽所有可能的整数,都不能满足要求,就得回退到前一方格,并调整前一方格填入的整数。如此重复执行扩展、检查或调整、检查,直到找到一个满足问题要求的解,将解输出. 回溯法找一个解的算法: int m=0,ok=1; int n=8; do if (ok) 扩展; else

30、调整; ok=检查前m个整数填放的合理性; while (!ok|m!=n)&(m!=0) if (m!=0) 输出解; else 输出无解报告; 如果程序要找全部解,则在将找到的解输出后,应继续调整最后位置上填放的整数,试图去找下一个解。相应的算法如下: 回溯法找全部解的算法: int m=0,ok=1; int n=8; do if (ok) if (m=n) 输出解; 调整; else 扩展; else 调整; ok=检查前m个整数填放的合理性; while (m!=0); 为了确保程序能够终止,调整时必须保证曾被放弃过的填数序列不会再次实验,即要求按某种有许模型生成填数序列.给解的候选

31、者设定一个被检验的顺序,按这个顺序逐一形成候选者并检验。从小到大或从大到小,都是可以采用的方法。如扩展时,先在新位置填入整数1,调整时,找当前候选解中下一个还未被使用过的整数。将上述扩展、调整、检验都编写成程序,细节见以下找全部解的程序。 【程序】 include stdio。h define N 12 void write(int a ) int i,j; for (i=0;i0;i+) if (m=primesi) return 1; for (i=3;ii=m;) if (m%i=0) return 0; i+=2; return 1; int checkmatrix 3= 1,0,-1

32、,1,-1,0,1,1,3,-1, 2,4,-1,3,-1,4,6,1,5,7,1; int selectnum(int start) int j; for (j=start;j=N;j+) if (bj) return j return 0; int check(int pos) int i,j; if (pos0) return 0; for (i=0;(j=checkmatrixposi)=0;i+) if (!isprime(apos+aj) return 0; return 1; int extend(int pos) a+pos=selectnum(1); bapos=0; 9 回

33、复:数据结构(ZT) return pos; int change(int pos) int j; while (pos=0&(j=selectnum(apos+1)=0) bapos-=1; if (pos=0) void main() int i; for (i=1;i=N;i+) bi=1; find(); 【问题】 n皇后问题 问题描述:求出在一个nn的棋盘上,放置n个不能互相捕捉的国际象棋“皇后”的所有布局. 这是来源于国际象棋的一个问题。皇后可以沿着纵横和两条斜线4个方向相互捕捉。如图所示,一个皇后放在棋盘的第4行第3列位置上,则棋盘上凡打“”的位置上的皇后就能与这个皇后相互捕捉。 1 2 3 4 5 6 7 8 Q 从图中可以得到以下启示:一个合适的解应是在每列、每

展开阅读全文
相似文档                                   自信AI助手自信AI助手
猜你喜欢                                   自信AI导航自信AI导航
搜索标签

当前位置:首页 > 包罗万象 > 大杂烩

移动网页_全站_页脚广告1

关于我们      便捷服务       自信AI       AI导航        获赠5币

©2010-2024 宁波自信网络信息技术有限公司  版权所有

客服电话:4008-655-100  投诉/维权电话:4009-655-100

gongan.png浙公网安备33021202000488号   

icp.png浙ICP备2021020529号-1  |  浙B2-20240490  

关注我们 :gzh.png    weibo.png    LOFTER.png 

客服