《算法设计与分析PPT课件.ppt》由会员分享,可在线阅读,更多相关《算法设计与分析PPT课件.ppt(390页珍藏版)》请在taowenge.com淘文阁网|工程机械CAD图纸|机械工程制图|CAD装配图下载|SolidWorks_CaTia_CAD_UG_PROE_设计图分享下载上搜索。
1、算法设计与分析算法设计与分析2课程目的n对算法设计与分析进行一个较为全面的介绍,使大家具有进行简单的算法设计与分析的基本能力先修课程n程序设计语言n数据结构n高等数学n离散数学n概率论3主要内容介绍n第1章算法引论n第2章递归与分治策略n第3章动态规划n第4章贪心算法n第5章回溯法n第6章分支限界法n第7章概率算法n第8章NP完全性理论4教学用书教学用书n王晓东n算法设计与分析n清华大学出版社5nT. Cormen, C. L Leiserson, R. R Rivest, and C. Steinn Introduction to Algorithms, 2nd Ed.nMIT Press
2、and McGraw-Hill Book Company, 2001教学参考书6nD. E. Knuth nThe Art of Computer Programming, Vol. 1 and 3, Third Edition, nAddison Wesley, 1997.7第1章 算法引论n程序=算法+数据结构1.1 算法与程序一. 算法在计算机科学中的重要地位8二.算法的基本概念一个有穷规则的有序集合称为一个算法。1.定义. 2.算法的特征.n输 入:有零个或多个外部量作为算法的输入。 n输 出:算法产生至少一个量作为输出。 n确定性:组成算法的每条指令清晰、无歧义。 n有限性:算法中每
3、条指令的执行次数有限。n可行性:执行每条指令的时间也有限。9例. 求正整数m、n的最大公因数。解一. (1)求余数:用m除以n,得余数r(0rn)。(2) 判断余数:若余数r=0,输出n,结束。 否则,转(3)。(3)更新被除数和除数:mn,nr,转(1)。10解二. 开 始输入m、nr=m%nr=0?mn,nr输出n是否11解三. Euclid(int m, int n) int r; while(n!=0) r=m%n; m=n; n=r; printf(“%d”, m) 123.算法的描述方法.(1)自然语言(2)流程图(3)程序设计语言13三.算法设计与分析的步骤1.问题的描述.2.模
4、型的选择.3.算法设计和正确性证明.4.算法的分析.5.算法的实现.141.2 算法复杂性分析 算法复杂性是算法运行所需要的计算机资源的量,需要时间资源的量称为时间复杂性时间复杂性,需要的空间资源的量称为空间复杂性空间复杂性。这个量应该只依赖于算法要解的问题的规模、算法的输入和算法本身的函数。如果分别用n、I和A表示算法要解问题的规模、算法的输入和算法本身,而且用C表示复杂性,那么,应该有C=F(n,I,A)。一般把时间复杂性和空间复杂性分开,并分别用T和S来表示,则有: T=T(n,I)和S=S(n,I) 。(通常,让A隐含在复杂性函数名当中) 15最坏情况下的时间复杂度:),(max)(m
5、axInTnTnDI),(*1Inetkiii),(*InT渐近时间复杂度:平均情况下的时间复杂性:nDIInTIPnT),()()(avgnDIkiiiInetIP),()(1 设Dn是规模为n的合法输入的集合;I*是Dn中使T(n, I*)达到Tmax(n)的合法输入;而P(I)是在算法的应用中出现输入I的概率。则:n时,T(n)的主要部分算法共有k种基本步骤,第i种步骤所需时间ti,出现次数ei.用问题体积n表示的运行时间T(n)称为时间复杂度)(nT16算法复杂度的重要性假设计算机每秒可作1000次基本运算17有效算法最佳算法计算问题的分类1.无法写出算法的问题.2.有多项式算法的问题
6、.3.介于上述两问题之间的问题.18例之值。求0111)(axaxaxaxPnnnnn解用最直观的方法2) 1() 121()(nnnnnT用Horner算法01321)()(axaxaxaaxaxPnnnnnnnT)( Horner(int an+1,real x) int p= an; for (i=1;i=n;i+) p=p*x+an-i; return p; 19表示算法渐近复杂度的数学符号:表示算法渐近复杂度的数学符号:渐近意义下的记号:O、o 设f(n)和g(n)是定义在正数集上的正函数。 O的定义的定义:如果存在正的常数C和自然数N0,使得当nN0时有f(n)Cg(n),则称函数
7、f(n)当n充分大时上有界,且g(n)是它的一个上界,记为f(n)=O(g(n)。即f(n)的阶不高于g(n)的阶。 根据O的定义,容易证明它有如下运算规则: (1)O(f)+O(g)=O(max(f,g); (2)O(f)+O(g)=O(f+g); (3)O(f)O(g)=O(fg); (4)如果g=O(f),则O(f)+O(g)=O(f); (5)O(Cf)=O(f),其中C是一个正的常数; (6)f=O(f)。 20 的定义的定义:如果存在正的常数C和自然数N0,使得当nN0时有f(n)Cg(n),则称函数f(n)当n充分大时下有界,且g(n)是它的一个下界,记为f(n)=(g(n)。即
8、f(n)的阶不低于g(n)的阶。 的定义的定义:定义f(n)=(g(n)当且仅当f(n)=O(g(n)且f(n)= (g(n)。此时称f(n)与g(n)同阶。 o o的定义的定义:对于任意给定的0,都存在正整数N0,使得当n N0时有f(n)/g(n),则称函数f(n)当n充分大时的阶比g(n)低,记为f(n)=o(g(n)。 例如,4nlogn+7=o(3n2+4nlogn+7)。 21凡治众如治寡,分数是也。凡治众如治寡,分数是也。 -孙子兵法孙子兵法222.1 n直接或间接地调用自身的较小模式的算法称为递归算法递归算法。n用函数自身的较小模式给出其定义的函数称为递归函数递归函数。n由分治
9、法产生的子问题往往是原问题的较小模式,子问题的复杂度也原问题复杂度的较小模式,这就为使用递归技术进行算法分析提供了方便。n分治与递归像一对孪生兄弟,经常同时应用在算法设计之中,并由此产生许多高效算法。23例例1 1 阶乘函数阶乘函数阶乘函数可递归地定义为:00)!1(1!nnnnn边界条件边界条件递归方程递归方程边界条件与递归方程是递归函数的二个要素,递归函数只有具备了这两个要素,才能在有限次计算后得出结果。下面来看几个实例: factorial(int n) if (n = 0) return 1; else return factorial(n-1); T(n)=T(n-1)+1,T(n)
10、=O(n)24nnn) 1(321!阶乘函数可以找到相应的非递归方式定义: factorial(int n) int i,p=1; for (i=1;i=n;i+) p=p*i; return p; 循环n次,故T(n)=n25Illustration例例2 Fibonacci2 Fibonacci数列数列26Fibonacci数列的前10项为1,1,2,3,5,8,13,21,34,55,它可以递归地定义为:边界条件边界条件递归方程递归方程第n个Fibonacci数可递归地计算如下: fibonacci(int n) if (n 1),和A(1,1)=2,得A(n,1)=2*nnm=2时,A
11、(n,2)=A(A(n-1,2),1)=2A(n-1,2),和A(1,2)=A(A(0,2),1)=A(1,1)=2,故A(n,2)= 。nm=3时,类似的可以推出nm=4时,A(n,4)的增长速度非常快,以至于没有适当的数学式子来表示这一函数。个nnA2222)3,(1,20)1), 1(),(2)0 ,(1),0(2)0 , 1 (mnnmmmnAAmnAnnAmAAn230n定义单变量的Ackerman函数A(n)为,A(n)=A(n,n)。n定义其拟逆函数(n)为:(n)=minkA(k)n。即(n)是使nA(k)成立的最小的k值。n(n)在复杂度分析中常遇到。对于通常所见到的正整数n
12、,有(n)4(n)4。但在理论上(n)没有上界,随着n的增加,它以难以想象的慢速度趋向正无穷大。个65536222)4 , 4(A31例例4 4 排列的生成问题排列的生成问题设计一个递归算法生成n个元素r1,r2,rn的全排列。设R=r1,r2,rn是要进行排列的n个元素,Ri=Rri。集合X中元素的全排列记为perm(X)。(ri)perm(X)表示在全排列perm(X)的每一个排列前加上前缀ri得到的排列。R的全排列可归纳定义如下: 当n=1时,perm(R)=(r),其中r是集合R中唯一的元素;当n1时,perm(R)由(r1)perm(R1),(r2)perm(R2),(rn)perm
13、(Rn)构成。 32perm( list, int k, int m)/产生前缀是产生前缀是list0:k-1,后缀是后缀是 listk:m的所有排列的所有排列 /perm(list,0,n-1)产生产生 list0: n-1的去全排列的去全排列 if (k=m) /单元素排列单元素排列 for (int i=0; i=m; i+) cout listi; cout endl; else /多元素序列,递归产生排列多元素序列,递归产生排列 for (int i=k; im1;正整数n的最大加数n1不大于m的划分由n1=m的划分和n1m-1 的划分组成。(3) q(1,m)=1,m1;3711,
14、 1),() 1,() 1,(1),(1),(mnmnmnmnmmnqmnqnnqnnqmnq显然,正整数n的划分数p(n)=q(n,n)。 q(int n,int m)if (m=1|n=1) return 1; else if (n 0) hanoi(n-1, a, c, b); move(a,b); hanoi(n-1, c, b, a); )2()(1) 1(2)(nOnTnTnT40hanoi(n, a, b, c)层次栈状态塔状态13,a,b,ccba32122,a,c,b3,a,b,ccba13231,a,b,c2,a,c,b3,a,b,c层次栈状态塔状态cba21322,a,c
15、,b3,a,b,ccba23131,c,a,b2,a,c,b3,a,b,c2,a,c,b3,a,b,c241层次栈状态塔状态层次栈状态塔状态13,a,b,ccba22,b,a,c3,a,b,c3 21cba23131,b,c,a2,b,a,c3,a,b,ccba31222,b,a,c3,a,b,ccba32131,a,b,c2,b,a,c3,a,b,c42cba32122,b,a,c3,a,b,c层次栈状态塔状态cba3213,a,b,c1层次栈状态塔状态cba321 栈空043优点:优点:结构清晰,可读性强,而且容易用数学归结构清晰,可读性强,而且容易用数学归纳法来证明算法的正确性,因此它为
16、设计算法、纳法来证明算法的正确性,因此它为设计算法、调试程序带来很大方便。调试程序带来很大方便。缺点:缺点:递归算法的运行效率较低,无论是耗费的递归算法的运行效率较低,无论是耗费的计算时间还是占用的存储空间都比非递归算法计算时间还是占用的存储空间都比非递归算法要多。要多。44解决方法:解决方法:在递归算法中消除递归调用,使其转在递归算法中消除递归调用,使其转化为非递归算法。化为非递归算法。1.1.采用一个用户定义的栈来模拟系统的递归调用采用一个用户定义的栈来模拟系统的递归调用工作栈。该方法通用性强,但本质上还是递归,工作栈。该方法通用性强,但本质上还是递归,只不过人工做了本来由编译器做的事情,
17、优化只不过人工做了本来由编译器做的事情,优化效果不明显。效果不明显。2.2.用递推来实现递归函数。用递推来实现递归函数。3.3.通过通过CooperCooper变换、反演变换能将一些递归转化变换、反演变换能将一些递归转化为尾递归,从而迭代求出结果。为尾递归,从而迭代求出结果。 后两种方法在时空复杂度上均有较大改善,后两种方法在时空复杂度上均有较大改善,但其适用范围有限。但其适用范围有限。45nT(n/2)T(n/2)T(n/2)T(n/2)T(n/2)T(n/2)T(n/2)T(n/2)T(n)=将问题分为a个子问题,对这a个子问题分别求解。如果子问题的规模仍然不够小,则每个子问题再划分为a个
18、子问题,如此递归的进行下去,直到问题规模足够小,很容易求出其解为止。46将求出的小规模的问题的解合并为一个更大规模的问题的解,自底向上逐步求出原来问题的解。分治算法的程序具有递归的特点nT(n)=n/2T(n/4)T(n/4)T(n/4)T(n/4)T(n/4)T(n/4)T(n/4)T(n/4)n/2T(n/4)T(n/4)T(n/4)T(n/4)T(n/4)T(n/4)T(n/4)T(n/4)n/2T(n/4)T(n/4)T(n/4)T(n/4)T(n/4)T(n/4)T(n/4)T(n/4)n/2T(n/4)T(n/4)T(n/4)T(n/4)T(n/4)T(n/4)T(n/4)T(n/
19、4)47n该问题的规模缩小到一定的程度就可以容易地解决;该问题的规模缩小到一定的程度就可以容易地解决;n该问题可以分解为若干个规模较小的相同问题,即该该问题可以分解为若干个规模较小的相同问题,即该问题具有问题具有最优子结构性质最优子结构性质n利用该问题分解出的子问题的解可以合并为该问题的利用该问题分解出的子问题的解可以合并为该问题的解;解;n该问题所分解出的各个子问题是相互独立的,即子问该问题所分解出的各个子问题是相互独立的,即子问题之间不包含公共的子问题。题之间不包含公共的子问题。 因为问题的计算复杂性一般是随着问题规模的增加而增加,因此大部分问题满足这个特征。这条特征是应用分治法的前提,它
20、也是大多数问题可以满足的,此特征反映了递归思想的应用能否利用分治法完全取决于问题是否具有这条特征,如果具备了前两条特征,而不具备第三条特征,则可以考虑贪心算法贪心算法或动态规划动态规划。这条特征涉及到分治法的效率,如果各子问题是不独立的,则分治法要做许多不必要的工作,重复地解公共的子问题,此时虽然也可用分治法,但一般用动态规划动态规划较好。48divide-and-conquer(P) if ( | P | = n0) adhoc(P); /解决小规模的问题 divide P into smaller subinstances P1,P2,.,Pa;/分解问题 for (i=1,i=a,i+)
21、 yi=divide-and-conquer(Pi); /递归的解各子问题 return merge(y1,.,ya); /将各子问题的解合并为原问题的解 人们从大量实践中发现,在用分治法设计算法时,最好使子问题的规模大致相同。即将一个问题分成大小相等的a个子问题的处理方法是行之有效的。这种使子问题规模大致相等的做法是出自一种平衡平衡(balancing)子问题子问题的思想,它几乎总是比子问题规模不等的做法要好。49一个分治法将规模为n的问题分成a个规模为n/b的子问题去解。设分解阀值n0=1,且adhoc解规模为1的问题耗费1个单位时间。再设将原问题分解为a个规模为b的子问题以及用merge
22、将这a个子问题的解合并为原问题的解需用f(n)个单位时间。用T(n)表示该分治法解规模为|P|=n的问题所需的计算时间,则有:11)()/() 1 ()(nnnfbnaTOnT通过迭代法求得方程的解:1log0log)/()(nbjjjabbnfannT注意注意:递归方程及其解只给出n等于b的方幂时T(n)的值,但是如果认为T(n)足够平滑,那么由n等于b的方幂时T(n)的值可以估计T(n)的增长速度。通常假定T(n)是单调上升的,从而当binbi+1时,T(bi)T(n)T(bi+1)。 50banObannObanOnTncnbnaTncnTbncbaab当当当的解为则方程的整次幂是是非负
23、常数,设定理),(),log(),()(1,)(1,)(.,.log51if T(n) = aT(n/b) + f(n) thenThe Master Theorem10largefor )()/( AND )()()()(log)(logloglogloglogcnncfbnafnnfnnfnOnfnfnnnnTaaaaabbbbb521log0log)/()()(:njjjabbbnfannTproof).()11()11()()()()/(),()(loglogloglog1log0log1log0loglog1log01log0loglogaanajnjajnjaanjnjajjjja
24、bbbbbbbbbbbbbnbnnbbnbnbabnbnabnfannfwhen)53).log() 1()()()/(),()(log1log0log1log0loglog1log01log0loglognnnbanbnabnfannfwhenbanjajnjaanjnjajjjjabbbbbbbbbb54) 1:.(11)()()()/()().()(),()(01log01log0logcattentioncnfcnfnfcbnfanfncfbnafANDnnfwhenjjnjnjjjjabbb55分析:如果n=1即只有一个元素,则只要比较这个元素和x就可以确定x是否在表中。因此这个问题
25、满足分治法的第一个适用条件分析:比较x和a的中间元素amid,若x=amid,则x在L中的位置就是mid;如果xai,同理我们只要在amid的后面查找x即可。无论是在前面还是后面查找x,其方法都和在a中查找x一样,只不过是查找的规模缩小了。这就说明了此问题满足分治法的第二个和第三个适用条件。 分析:很显然此问题分解出的子问题相互独立,即在ai的前面或后面查找x是独立的子问题,因此满足分治法的第四个适用条件。给定已按升序排好序的给定已按升序排好序的n个元素个元素a0:n-1,现要在这,现要在这n个元素中找个元素中找出一特定元素出一特定元素x。分析:分析: 该问题的规模缩小到一定的程度就可以容易地
26、解决;该问题的规模缩小到一定的程度就可以容易地解决;该问题可以分解为若干个规模较小的相同问题该问题可以分解为若干个规模较小的相同问题;分解出的子问题的解可以合并为原问题的解;分解出的子问题的解可以合并为原问题的解;分解出的各个子问题是相互独立的。分解出的各个子问题是相互独立的。 56给定已按升序排好序的给定已按升序排好序的n个元素个元素a0:n-1,现要在这,现要在这n个元素中找个元素中找出一特定元素出一特定元素x。据此容易设计出二分搜索算法二分搜索算法: BinarySearch(int a, int x, int n) / 在 a0 = a1 = . = an-1 中搜索 x / 找到x时
27、返回其在数组中的位置,否则返回-1 int left = 0; int right = n - 1; while (left amiddle) left = middle + 1; else right = middle - 1; return -1; / 未找到x 算法复杂度分析:算法复杂度分析:每执行一次算法的while循环, 待搜索数组的大小减少一半。因此,在最坏情况下,while循环被执行了O(log2n) 次。循环体内运算需要O(1) 时间,因此整个算法在最坏情况下的计算时间复杂性为O(log2n) 。8765432157 请设计一个有效的算法,可以进行两个请设计一个有效的算法,可以
28、进行两个n n位大整数的乘法运算位大整数的乘法运算u小学的方法:O(n2) 效率太低u分治法: abcdX = Y = X = a 2n/2 + b Y = c 2n/2 + d XY = ac 2n + (ad+bc) 2n/2 + bd 复杂度分析复杂度分析T(n)=O(n2) 没有改进没有改进11)()2/(4) 1 ()(nnnOnTOnT58由:XY = ac 2n + (ad+bc) 2n/2 + bd 得:XY = ac 2n + (a-b)(d-c)+ac+bd) 2n/2 + bd或:XY = ac 2n + (a+b)(c+d)-ac-bd) 2n/2 + bd复杂度分析复
29、杂度分析T(n)=O(nlog3) =O(n1.59) 较大的改进较大的改进11)()2/(3) 1 ()(nnnOnTOnT细节问题细节问题:两个XY的复杂度都是O(nlog3),但考虑到a+c,b+d可能得到m+1位的结果,使问题的规模变大,故不选择第2种方案。为了降低时间复杂度,必须减少乘法的次数。59 请设计一个有效的算法,可以进行两个请设计一个有效的算法,可以进行两个n n位大整数的乘法运算位大整数的乘法运算u小学的方法:O(n2) 效率太低u分治法: O(n1.59) 较大的改进u更快的方法?如果将大整数分成更多段,用更复杂的方式把它们组合起来,将有可能得到更优的算法。最终的,这个
30、思想导致了快速傅利叶变换快速傅利叶变换(Fast Fourier Transform)的产生。该方法也可以看作是一个复杂的分治算法,对于大整数乘法,它能在O(nlogn)时间内解决。是否能找到线性时间的算法?目前为止还没有结果。60A和B的乘积矩阵C中的元素Ci,j定义为: nkjkBkiAjiC1若依此定义来计算A和B的乘积矩阵C,则每计算C的一个元素Cij,需要做n次乘法和n-1次加法。因此,算出矩阵C的 个元素所需的计算时间为O(n3)u传统方法61使用与上例类似的技术,将矩阵A,B和C中每一矩阵都分块成4个大小相等的子矩阵。由此可将方程C=AB重写为:u分治法2221121122211
31、21122211211BBBBAAAACCCC由此可得:2112111111BABAC2212121112BABAC2122112121BABAC2222122122BABAC复杂度分析复杂度分析T(n)=O(n3) 没有改进没有改进22)()2/(8) 1 ()(2nnnOnTOnT62为了降低时间复杂度,必须减少乘法的次数。)(2212111BBAM2212112)(BAAM1122213)(BAAM)(1121224BBAM)(221122115BBAAM)(222122126BBAAM)(121121117BBAAM624511MMMMC2112MMC4321MMC731522MMMM
32、C222112112221121122211211BBBBAAAACCCC对变形复杂度分析复杂度分析T(n)=O(nlog7) =O(n2.81) 较大的改进较大的改进22)()2/(7) 1 ()(2nnnOnTOnT63u传统方法:O(n3)u分治法: O(n2.81)u更快的方法?Hopcroft和Kerr已经证明(1971),计算2个矩阵的乘积,7次乘法是必要的。因此,要想进一步改进矩阵乘法的时间复杂性,就不能再基于计算22矩阵的7次乘法这样的方法了。或许应当研究或矩阵的更好算法。在Strassen之后又有许多算法改进了矩阵乘法的计算时间复杂性。目前最好的计算时间上界是 O(n2.37
33、6)是否能找到O(n2)的算法?目前为止还没有结果。64在一个2k2k 个方格组成的棋盘中,恰有一个方格与其他方格不同,称该方格为一特殊方格,且称该棋盘为一特殊棋盘。在棋盘覆盖问题中,要用图示的4种不同形态的L型骨牌覆盖给定的特殊棋盘上除特殊方格以外的所有方格,且任何2个L型骨牌不得重叠覆盖。65当k0时,将2k2k棋盘分割为4个2k-12k-1 子棋盘(a)所示。特殊方格必位于4个较小子棋盘之一中,其余3个子棋盘中无特殊方格。为了将这3个无特殊方格的子棋盘转化为特殊棋盘,可以用一个L型骨牌覆盖这3个较小棋盘的会合处,如 (b)所示,从而将原问题转化为4个较小规模的棋盘覆盖问题。递归地使用这种
34、分割,直至棋盘大小为11。 66 ChessBoard(int tr, int tc, int dr, int dc, int size) / tr、tc:棋盘左上角方格的行号、列号 /dr、dc:特殊方格的行号、列号 if (size = 1) return; int t = tile+; / L型骨牌号,tile初值为1 int s = size/2; / 分割棋盘 / 用L型骨牌号覆盖左上角子棋盘 if (dr tr + s & dc tc + s) / 特殊方格在此棋盘中 chessBoard(tr, tc, dr, dc, s); else /特殊方格不在此棋盘中 / 用 t 号L型
35、骨牌覆盖右下角 boardtr + s - 1tc + s - 1 = t; / 覆盖本子棋盘中的其余方格 chessBoard(tr, tc, tr+s-1, tc+s-1, s); /用L型骨牌号覆盖右上角子棋盘 if (dr = tc + s) / 特殊方格在此棋盘中 chessBoard(tr, tc+s, dr, dc, s); else /特殊方格不在此棋盘中 / 用 t 号L型骨牌覆盖左下角 boardtr + s - 1tc + s = t; / 覆盖本子棋盘中的其余方格 chessBoard(tr, tc+s, tr+s-1, tc+s, s); /用L型骨牌号覆盖左下角子棋
36、盘 if (dr = tr + s & dc = tr + s & dc = tc + s) / 特殊方格在此棋盘中 chessBoard(tr+s, tc+s, dr, dc, s); else / 用 t 号L型骨牌覆盖左上角 boardtr + stc + s = t; / 覆盖本子棋盘中的其余方格 chessBoard(tr+s, tc+s, tr+s, tc+s, s); 复杂度分析复杂度分析T(n)=O(4k) 渐进意义下的最优算法00) 1 () 1(4) 1 ()(kkOkTOkT67222chessBoard(0, 0, 0, 1, 2)chessBoard(0, 0, 0,
37、 1, 1)chessBoard(0, 0, 0, 1, 2)1chessBoard(0, 0, 0, 1, 1)333chessBoard(0, 0, 0, 1, 2)1chessBoard(0, 0, 0, 1, 1)444chessBoard(0, 0, 0, 1, 2)1chessBoard(0, 0, 0, 1, 1)555例例chessBoard(0, 0, 0, 1, 4)68基本思想:基本思想:将待排序元素分成大小大致相同的2个子集合,分别对2个子集合进行排序,最终将排好序的子集合合并成为所要求的排好序的集合。 MergeSort(int a, int left, int ri
38、ght) if (leftright) /至少有2个元素 int i=(left+right)/2; /取中点 mergeSort(a, left, i); mergeSort(a, i+1, right); merge(a, b, left, i, right); /合并到数组b copy(a, b, left, right); /复制回数组a 复杂度分析复杂度分析T(n)=O(nlogn) 渐进意义下的最优算法11)()2/(2) 1 ()(nnnOnTOnT69A = ;, , , , ;Show Merge_Sort( ) running on the arrayExample ; ;
39、70&最坏时间复杂度:最坏时间复杂度:O(nlogn)&平均时间复杂度:平均时间复杂度:O(nlogn)&辅助空间:辅助空间:O(n)&稳定性:稳定稳定性:稳定思考题:给定有序表思考题:给定有序表A1:n,修,修改合并排序算法,求出该有序表改合并排序算法,求出该有序表的逆序对数。的逆序对数。1),()2(21),1()(nnOnTnOnT71在快速排序中,记录的比较和交换是从两端向中间进行的,关键字较大的记录一次就能交换到后面单元,关键字较小的记录一次就能交换到前面单元,记录每次移动的距离较大,因而总的比较和移动次数较少。qSort(int p, int r) if (pr) int q=pa
40、rtition(p,r); /以ap为基准元素将ap:r划分成3段ap:q-1,aq和aq+1:r,使得ap:q-1中任何元素小于等于aq,aq+1:r中任何元素大于aq。下标q在划分过程中确定。 qSort (p,q-1); /对左半段排序 qSort (q+1,r); /对右半段排序 快速排序是对气泡排序的一种改进方法它是由C.A.R. Hoare于1962年提出的72快速排序具有不稳定性不稳定性。partition2, 5, 5, 6, 7, 8完成partition (int p, int r) int i = p; int j = r + 1; int x = ap; / 将x的元素
41、交换到右边区域 while (true) while (a+i x); if (i = j) break; swap(a, i, j); ap = aj; aj = x; return j; 6, 7, 5, 2, 5, 8i+j-;ijj初始序列6, 7, 5, 2, 5, 8 i6, 7, 5, 2, 5, 8j-;ij6,5, 5, 2, 7, 8ji交换交换i+;i+;i+;j-;ji6,5, 5, 2, 7, 8例 用快速排序法将6,7,5,2,5,8排序73randomizedPartition (int p, int r) int i = random(p,r); swap(a,
42、 i, p); return partition (p, r); 快速排序算法的性能取决于划分的对称性。通过修改算法partition,可以设计出采用随机选择策略的快速排序算法。在快速排序算法的每一步中,当数组还没有被划分时,可以在ap:r中随机选出一个元素作为划分基准,这样可以使划分基准的选择是随机的,从而可以期望划分是较对称的。&最坏时间复杂度:最坏时间复杂度:O(n2)&平均时间复杂度:平均时间复杂度:O(nlogn)&稳定性:不稳定稳定性:不稳定74快速排序时间复杂度的分析快速排序时间复杂度的分析n最坏情况?n分割总是最不平衡n最好情况?n每次将数组对半分割n最可能情况?n介于上述两者
43、之间T(1) = (1)T(n) = T(n-2) + (n)T(n) = (n2)T(n) = 2T(n/2) + (n)T(n) = (nlogn) T(n) = 2T(n/b) + (n),bbacbc acacbcbcoutput c,b,aoutput b,c,aoutput b,a,coutput c,a,boutput a,c,boutput a,b,cExample83n设树的高度为设树的高度为h,则:,则: n! 2h log(n!) h 由由Stirling公式公式:nenn!从而:定理定理 具有具有 n!个叶结点!个叶结点的决策树的高度为的决策树的高度为 (nlogn)n
44、nennnenhnloglogloglog84给定线性序集中n个元素和一个整数k,1kn,要求找出这n个元素中第k小的元素randomizedSelect(int p,int r,int k) if (p=r) return ap; int i=randomizedpartition(p,r); j=i-p+1;/基准元素ai的序数 if (k=j) return randomizedSelect(p,i,k); else return randomizedSelect(i+1,r,k-j); 下面证明:在最坏情况下,算法randomizedSelect需要O(n2)计算时间,但在平均情况下,
45、算法randomizedSelect可以在O(n) 时间内找出n个输入元素中的第k小元素。85nRandomizedSelect()的时间复杂度的时间复杂度n最坏情形: 数组总被分为 1:n-1T(n) = T(n-1) + O(n)= ? = O(n2) n比先排序还差!n最好情形:数组总被分为 9:1 T(n) = T(9n/10) + O(n) = ? = O(n)(Master Theorem)n优于排序!n数组总被分为 99:1 会怎样?86n平均时间复杂度n假定要找的元素总落在较大的子数组中: 12/1112,max11nnknknkTnnknkTnnTn下面用归纳法证明下面用归纳
46、法证明 T(n) = O(n) 87n设存在数设存在数c,使得对,使得对kn有:有:T(k) ck,则:,则: nnccnnnnnnncnkkncncknnkTnnTnknknnknnk421221121121212)(12)(1211112/12/ cnnnc4388如果能在线性时间内找到一个划分基准,使得按这个基准所划分出的2个子数组的长度都至少为原数组长度的倍(01是某个正常数),那么就可以在最坏情况下在最坏情况下用O(n)时间完成选择任务。例如,若=9/10,算法递归调用所产生的子数组的长度至少缩短1/10。所以,在最坏情况下,算法所需的计算时间T(n)满足递归式T(n)T(9n/10
47、)+O(n) 。由此Master定理可得T(n)=O(n)。寻找基准元素89l将n个输入元素划分成n/5个组,每组5个元素,只可能有一个组不是5个元素。用任意一种排序算法,将每组中的元素排好序,并取出每组的中位数,共n/5个。l递归调用select来找出这n/5个元素的中位数(如果n/5是偶数,就找它的2个中位数中较大的一个)。以这个元素作为划分基准。设所有元素互不相同。在这种情况下,n/5个中位数中有 (n-5)/10个数大于基准x。因此,n个数中至少有3 (n-5)/10个数大于基准x。同理,至少有3 (n-5)/10 个元素小于基准x。而当n75时, 3(n-5)/10 n/4。所以,按
48、基准x划分所得的2个子数组的长度都至少缩短1/4。5/n2/5/n10/ )5(3n10/ )5(3n10/ )5(3n10/ )5( n90select(int p, int r, int k) if (r-p75) /用某个简单排序算法对数组ap:r排序; bubbleSort(p,r); return ap+k-1; /将ap+5*i至ap+5*i+4的第3小元素 /与ap+i交换位置; /找中位数的中位数,r-p-4即上面所说的n-5 for ( int i = 0; i=(r-p-4)/5; i+ ) int s=p+5*i; t=s+4; for (int j=0;j3;j+) b
49、ubble(s,t-j);/冒泡3次即可找到第3小元素 swap(a, p+i, s+2); x = select(p, p+(r-p-4)/5, (r-p+6)/10);/ (r-p+6)/10=p+p+(r-p-4)/5/2+1,T(n/5) int i=partition(p,r,x), j=i-p+1; if (k=j) return select(p,i,k);/T(3n/4) else return select(i+1,r,k-j); 复杂度分析复杂度分析T(n) 20Cn=O(n)7575)4/3()5/()(21nnnTnTnCCnT上述算法将每一组的大小定为5,并选取75作
50、为是否作递归调用的分界点。这两点保证了T(n)的递归式中2个自变量之和n/5+3n/4=19n/20=n,01。这是使T(n)=O(n)的关键之处。当然,除了5和75之外,还有其他选择。9192动态规划算法与分治法类似,其基本思想也是将待求解问题分解成若干个子问题,而问题的解可用子问题的解表示,即要解决的问题具有最优子结构性质最优子结构性质nT(n/2)T(n/2)T(n/2)T(n/2)T(n/2)T(n/2)T(n/2)T(n/2)T(n)=93但是经分解得到的子问题往往不是互相独立的。在用分治法求解时,有些子问题被反复计算多次。这种性质称为子问题的重叠性质子问题的重叠性质。如果能够保存已