1 / 90

声明: 本课件版权归 清华大学计算机系语音技术中心 所有 未经许可 不得扩散

声明: 本课件版权归 清华大学计算机系语音技术中心 所有 未经许可 不得扩散. 递归算法举例. 习题讨论. 青蛙过河. 快速排序. 分书问题. m<n|| n==1 c(m,n) m==0||n==0 n==m n!=m 0 m 1 c(m-1,n) c(m-1,n-1) c(m-1,n)+

leanna
Download Presentation

声明: 本课件版权归 清华大学计算机系语音技术中心 所有 未经许可 不得扩散

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. 声明: 本课件版权归 清华大学计算机系语音技术中心 所有 未经许可 不得扩散

  2. 递归算法举例 • 习题讨论 • 青蛙过河 • 快速排序 • 分书问题

  3. m<n||n==1 c(m,n) m==0||n==0 n==m n!=m 0 m 1 c(m-1,n) c(m-1,n-1) c(m-1,n)+ c(m-1,n-1)

  4. // ************************************* // * 程 序:6_7.cpp * // * 编制时间:2002年10月28日 * // * 主要功能:计算组和数C(m,n) * // ************************************* #include <iostream> // 预编译命令 Using namespace std;

  5. // 计算C(m,n),即从m个数中取n个数的组合数 int C ( int m, int n) { if (m<0 || n<0 || m<n) return 0; if (m==n) // C(m,m)=1 return 1; if (n==1) // C(m,1)=m return m; // C(m,n)=C(m-1,n)+C(m-1,n-1) return C (m-1, n)+C (m-1,n-1); }

  6. int Cmn( int m, int n) { if (m<0 || n<0 || m<n) return 0; if (m==n) // C(m,m)=1 return 1; if (n==1) // C(m,1)=m return m; return Cmn(m-1, n)+Cmn(m-1,n-1); }

  7. int main() // 主函数开始 { // 测试一些结果 cout << "C(6,0)=" << Cmn(6,0) << endl; cout << "C(6,1)=" << Cmn(6,1) << endl; cout << "C(6,2)=" << Cmn(6,2) << endl; cout << "C(6,6)=" << Cmn(6,6) << endl; return 0; } // 主函数结束

  8. int C ( int m, int n) { if (m<0 || n<0 || m<n) return 0; if (m==n) // C(m,m)=1 return 1; if (n==1) // C(m,1)=m return m; // C(m,n)=C(m-1,n)+C(m-1,n-1) return C (m-1, n)+C (m-1,n-1); }

  9. 递 归 算 法 举 例——青蛙过河

  10. 讨论问题——青蛙过河 该题是2000年全国青少年信息学奥林匹克的一道试题。叙述如下: 一条小溪尺寸不大,青蛙可以从左岸跳到右岸,在左岸有一石柱L,面积只容得下一只青蛙落脚,同样右岸也有一石柱R,面积也只容得下一只青蛙落脚。有一队青蛙从尺寸上一个比一个小。我们将青蛙从小到大,用1,2,…,n编号。规定初始时这队青蛙只能趴在左岸的石头L上,按编号一个落一个,小的落在大的上面。不允许大的在小的上面。在小溪中有S个石柱,有y片荷叶,规定溪中的柱子上允许一只青蛙落脚,如有多只同样要求按编号一个落一个,大的在下,小的在上,而且必须编号相邻。对于荷叶只允许一只青蛙落脚,不允许多只在其上。对于右岸的石柱R,与左岸的石柱L一样允许多个青蛙落脚,但须一个落一个,小的在上,大的在下,且编号相邻。当青蛙从左岸的L上跳走后就不允许再跳回来;同样,从左岸L上跳至右岸R,或从溪中荷叶或溪中石柱跳至右岸R上的青蛙也不允许再离开。问在已知溪中有S根石柱和y片荷叶的情况下,最多能跳过多少只青蛙?

  11. 思路: 1、简化问题,探索规律。先从个别再到一般,要善于对多个因素作分解,孤立出一个一个因素来分析,化难为易。 2. 定义函数 Jump ( s ,y ) —— 最多可跳过河的青蛙数 其中: S —— 河中柱子数 y —— 荷叶数

  12. 3. 先看简单情况,河中无柱子:S = 0, Jump ( 0 , y ) 当 y = 1 时,Jump ( 0 , 1 ) = 2 ; 第一步:1# 跳到荷叶上; 第二步:2# 从 L 直接跳至 R 上; 第三步:1# 再从荷叶跳至 R 上。 如下图: 1# 2#

  13. 当 y = 2 时, Jump ( 0 , 2 ) = 3 ; 1#,2#,3# 3只青蛙落在 L 上, 第一步:1# 从 L 跳至叶 1上, 第二步:2# 从 L 跳至叶 2上, 第三步:3# 从 L 直接跳至 R 上, 第四步:2# 从叶 2 跳至 R 上, 第五步:1# 从叶 1 跳至 R 上, 采用归纳法: Jump ( 0 , y ) = y+1;

  14. 再看Jump( S, y )先看一个最简单情况: S = 1,y = 1 。 从图上看出需要 9 步,跳过 4 只青蛙。 1# 青蛙从 L -> Y;2# 青蛙从 L -> S;1# 青蛙从 Y -> S;3# 青蛙从 L -> Y;4# 青蛙从 L -> R;3# 青蛙从 Y -> R;1# 青蛙从 S -> Y;2# 青蛙从 S -> R;1# 青蛙从 Y -> R;

  15. 表一

  16. 为了将过河过程描述得更清楚,我们给出了表1。表中L1 L2 L3 L4表示左岸石柱上落在一起的青蛙的高度位置。L1 在最上面,L4 在最下面的位置。引入这个信息就可比较容易地看出对青蛙占位的约束条件。同理R1 R2 R3 R4也是如此。对水中石柱S,也分成两个高度位置S1 S2。对荷叶Y无须分层,因为它只允许一只青蛙落在其上。t=0为初始时刻,青蛙从小到大落在石柱L上。t=1为第一步:1#从L跳至荷叶Y上;L上只剩2# 3# 4#。T=2 为第二步;2#从L跳至石柱S上,处在S2位置上,L上只剩3#和4#。T=3为第三步,1#从Y跳至S,将Y清空。这时你看,S上有1#、2#,L上有3#、4#,好象是原来在L上的4只青蛙,分成了上下两部分,上面的2只通过荷叶y转移到了S上。这一过程是一分为二的过程。即将L上的一队青蛙,分解为两个队,每队各二只,且将上面的二只转移到了S上。这时我们可以考虑形成两个系统,一个是L,Y,R系统,一个是S,Y,R系统。前者二只青蛙号大;后者二只青蛙号小。先跳号大的,再跳号小的。从第五步到第九步可以看出的确是这么做的。

  17. 2 Y L R 3 S 1

  18. L-Y-S ,将 L上的一半青蛙转移到 S 上 • L-Y-R,将 L上的青蛙转移到 R 上 • S-Y-R,将 S 上的青蛙转移到 R 上 • 对于LYR系统,相当于Jump(0,1)对于SYR系统,相当于Jump(0,1) • 两个系统之和为2*Jump(0,1),因此有:Jump(1,1)=2*Jump(0,1)=2*2=4

  19. 现在再看S=2,y=1 Jump(2,1) 我们将河中的两个石柱称作S1和S2,荷叶叫y,考虑先将L上的青蛙的一半借助于S2和y转移到S1上,当然是一半小号的青蛙在S1上,大的留在L上。

  20. S=2, y=1: S=S1+S2 S1=S2=S-1 L Y R S2 S1 23 1

  21. L-S2-Y-S1 以S1为跳转的目的地,从L经S2Y到S1,相当于JAMP(1,1)=4, 即S1 上有4 只青蛙,L上还保留4 只。 1 2 Y 3 4 5 1 6 2 L 7 S2 1 3 S1 8 2 4

  22. 1 1 2 2 3 3 4 4 5 6 7 8 R Y L S1 S2

  23. Y R L S2 S1 LS2YR S1S2YR

  24. 这样 L S1 S2 y R系统分解为 : (L S2 y R系统) +(S1 S2 y R系统)= 2*(L S2 y R系统)= 2 * Jump(1,1) 用归纳法Jump(S, y)=2*Jump(S-1, y)

  25. 5. 将上述分析出来的规律写成递归形式的与或结点图为:

  26. 举例:S=3,y=4,算 Jump(3,4)

  27. // ******************************************* // * 程 序:6_5.cpp * // * 作 者:wuwh * // * 编制时间:2002年10月20日 * // * 主要功能:青蛙过河 * // ******************************************* #include <iostream.h> //预编译命令 int Jump(int, int); //声明有被调用函数 void main() //主函数 { //主程序开始 int s=0,y=0,sum=0;//整型变量,s为河中石柱数,y为荷叶数 cout << "请输入石柱数s="; //提示信息 cin >> s; //输入正整数s cout << "请输入荷叶数y="; //提示信息 cin >> y; //输入正整数y sum=Jump(s,y); //Jump(s,y)为被调用函数 cout << "Jump(" << s << "," //输出结果 << y << ")="<< sum << endl; } //主程序结束

  28. // ****************************** // * 程 序:6_5.cpp * // * 作 者:wuwh * // * 编制时间:2002年10月20日 * // * 主要功能:青蛙过河(递归) * // ******************************

  29. #include <iostream.h> //预编译命令 int Jump(int, int); //声明有被调用函数 int main() //主函数 { int s=0,y=0,sum=0; cout << "请输入石柱数s="; //提示 cin >> s; //输入正整数s cout << "请输入荷叶数y="; //提示信息 cin >> y; //输入正整数y sum=Jump(s,y); //Jump(s,y)为被调用函数 cout << "Jump(" << s << "," //输出结果 << y << ")="<< sum << endl; }

  30. //以下函数是被主程序调用的函数 intJump ( int r, int z ) //自定义函数, r , z 为形参 { //自定义函数体开始 int k=0; //整型变量 if (r==0) //如果 r 为 0 ,则为直接可解结点, k = z + 1; //直接可解结点, k 值为 z + 1 else//如果r不为0,则要调用Jump( r-1, z ) k=2*Jump(r-1,z); return ( k ) ;//将 k 的值返回给Jump ( s , y ) } //自定义函数体结束

  31. 递 归 算 法 举 例——快速排序

  32. 快速排序的思路: 1、将待排序的数据放入数组 a 中,下标从 z 到 y ; 2、取 a[ z ] 放变量 k 中,通过分区处理,为 k 选择应该排定的位置。这时要将比 k 大的数放右边,比 k 小的数放左边。当 k 到达最终位置后,由 k 划分了左右两个集合。然后再用同样的思路处理左集合与右集合。 3、令sort( z ,y )为将数组元素从下标为 z 到下标为 y 的 y – z + 1 个元素从小到大排序。

  33. z y k z m y m-1 m+1 z m-1 m m+1 y

  34. 我们画出与或图来阐述快速排序的思路:

  35. A sort( z,y ) z>=y z<y B C 不做事 D E F 分区处理 sort(z,m-1) sort(m+1,y)

  36. 分区处理: 1、让 k=a[ z ] 2、将 k 放在 a[ m ] 3、使 a[z], a[z+1 ], …, a[ m-1 ] <= a[ m ] 4、使 a[ m ] < a[ m+1 ], a[ m+2 ], …, a[y] A 结点表示将数组 a[ z ], a[ z+1 ], …, a[ y ]中的元素 按由小到大用快速排序的思路排序。 B 结点表示如果 z >= y,则什么也不做。这是直接可解结点。 C 结点是在 z< y 情况下 A 结点的解。C 是一个与结点。要对 C 求解需分解为三步。依次为:

  37. 1、先解 D 结点,D 结点是一个直接可解结点,功能是进行所谓的分区处理,规定这一步要做的事情是 • (1)将 a[ z ] 中的元素放到它应该在的位置上,比如 m 位置。这时 a[ m ]  a [ z ] ; • (2)让下标从 z 到 m-1 的数组元素小于等 a[ m ]; • (3)让下标从 m+1 到 y 的数组元素大于a[ m ]; • 比如 a 数组中 a[ z ] = 5,经分组处理后,5 送至 a[ 4 ]。5 到位后,其左边 a[ 0 ]~a[ 3 ] 的值都小于 5;其右边 a[ 5 ], a[ 6 ] 大于 5。 • (见下图)

  38. y z a 下标: m a 下标: z m-1 m+1 y

  39. 2、再解 E 结点,这时要处理的是 a[ 0 ]~a[ 3 ]; 3、再解 F 结点,处理a[ 5 ],a[ 6 ]。 下面按照这种思路构思一个快速排序的程序框图。 void sort( int array[ ], int zz, int yy ) int z, y, i , k ;

  40. 下面举例说明排序过程 图1 a数组中有7个元素待排序 1 让 k = a[ z ] = a[ 0 ] = 5 k y z 图 1

  41. 2 进入直到型循环 • 执行(1)a[y]=a[6]=4 不满足当循环条件,y不动。 • 执行(2)z<y,做两件事: • a[ z ] = a[ y ],即a[ 0 ] = a[ 6 ] = 4, • z = z +1 = 0+1 = 1,见图2 k y z 图 2

  42. 执行(3)图2中的a[1]<k,满足当循环条件,z=z+1=2,z增1后的情况如图3。图3的情况不再满足当循环条件。执行(3)图2中的a[1]<k,满足当循环条件,z=z+1=2,z增1后的情况如图3。图3的情况不再满足当循环条件。 k y z 图 3

  43. 执行(4)a[y]=a[z],即a[6]=a[2]=6,见图4。这时 z != y 还得执行直到型循环的循环体。 k y z 图 4

  44. 执行(1)a[y]=a[6]=6,6>k满足当循环的条件, y = y-1 = 6-1 = 5 见图5,之后退出当循环,因为 a[ y ] = 3<k k y z 图 5

  45. 执行(2)a[ z ]=a[ y ],并让 z = z+1=3,见图6 k y z 图 6

  46. 执行(3)由于a[3]=1<k,满足当循环条件, • 让 z = z+1=4。a[4]=7>k,退出循环。见图7 k y z 图 7

  47. 执行(4)a[z]=a[y],a[5]=7。见图8 这时仍然 z<y ,应继续执行直到型循环的循环体。 k y z 图 8

  48. 执行(1),a[ y ] = 7>k,让 y = y-1 = 4。见图9 k y z 图 9

More Related