当前位置:文档之家› 函数的递归调用与分治策略

函数的递归调用与分治策略

函数的递归调用与分治策略
函数的递归调用与分治策略

函数的递归调用与分治策略

递归方法是算法和程序设计中的一种重要技术。递归方法即通过函数或过程调用自身将问题转化为本质相同但规模较小的子问题。递归方法具有易于描述和理解、证明简单等优点,在动态规划、贪心算法、回溯法等诸多算法中都有着极为广泛的应用,是许多复杂算法的基础。递归方法中所使用的“分而治之”的策略也称分治策略。

递归方法的构造

构造递归方法的关键在于建立递归关系。这里的递归关系可以是递归描述的,也可以是递推描述的。下面由一个求n的阶乘的程序为例,总结出构造递归方法的一般步骤。

[例1]从键盘输入正整数N(0<=N<=20),输出N!。

[分析]N!的计算是一个典型的递归问题。使用递归方法来描述程序,十分简单且易于理解。

[步骤1]描述递归关系递归关系是这样的一种关系。设{U1,U2,U3,…,Un…}是一个序列,如果从某一项k开始,Un和它之前的若干项之间存在一种只与n有关的关系,这便称为递归关系。

注意到,当N>=1时,N!=N*(N-1)!(N=1时,0!=1),这就是一种递归关系。对于特定的K!,它只与K与(K-1)!有关。

[步骤2]确定递归边界在步骤1的递归关系中,对大于k的Un的求解将最终归结为对Uk的求解。这里的Uk称为递归边界(或递归出口)。在本例中,递归边界为k=0,即0!=1。对于任意给定的N!,程序将最终求解到0!。

确定递归边界十分重要,如果没有确定递归边界,将导致程序无限递归而引起死循环。例如以下程序:

#include

int f(int x){

return(f(x-1));

}

main(){

cout<

}

它没有规定递归边界,运行时将无限循环,会导致错误。

[步骤3]写出递归函数并译为代码将步骤1和步骤2中的递归关系与边界统一起来用数学语

言来表示,即

N*(N-1)! 当N>=1时

n!=

1 当N=0时

再将这种关系翻译为代码,即一个函数:

long f(int n){

if (n==0)

return(1);

else

return(n*f(n-1));

}

[步骤4]完善程序主要的递归函数已经完成,将程序依题意补充完整即可。//ex1.cpp

#include

long f(int n){

if (n==0)

return(1);

else

return(n*f(n-1));

}

main(){

int n;

cin>>n;

cout<

}

综上,得出构造一个递归方法基本步骤,即描述递归关系、确定递归边界、写出递归函数并译为代码,最后将程序完善。以下继续引用一些例子来讨论递归方法的应用。

经典递归问题

以下讨论两个十分经典的递归问题。它们的算法构造同样遵循刚刚得出的四个步骤。了解这两个问题可加深对递归方法的理解。

[例2]Fibonacci数列(兔子繁殖)问题:已知无穷数列A,满足:A(1)=A(2)=1,A(N)=A(N-1)+A(N-2)(N>=3)。从键盘输入N,输出A(N)。

[分析]递归关系十分明显,由A(N)的表达式给出。需要注意的是本例中对于N>=3,A(N)的值与A(N-1)和A(N-2)都有关。

[代码]

//ex2.cpp

#include

long fibonacci(int x)

{

if ( (x==1) || (x==2) )

return(1);

else

return(fibonacci(x-1)+fibonacci(x-2));

}

main(){

int n;

cin>>n;

cout>>endl>>fibonacci(n);

}

[例3]Hanoi塔问题。

[问题描述]在霍比特人的圣庙里,有一块黄铜板,上面插着3根宝石针(分别为A号,B 号和C号)。在A号针上从下到上套着从大到小的n个圆形金片。现要将A针上的金片全部移到C针上,且仍按照原来的顺序叠置。移动的规则如下:这些金片只能在3根针间移动,一次只能一片,且任何时候都不允许将较大的金片压在较小的上面。从键盘输入n,要求给出移动的次数和方案。

[分析]由金片的个数建立递归关系。当n=1时,只要将唯一的金片从A移到C即可。当n>1时,只要把较小的(n-1)片按移动规则从A移到B,再将剩下的最大的从A移到C(即中间“借助”B把金片从A移到C),再将B上的(n-1)个金片按照规则从B移到C(中间“借助”A)。

本题的特点在于不容易用数学语言写出具体的递归函数,但递归关系明显,仍可用递归方法求解。

[代码]

//ex3.cpp

#include

hanoi(int n,char t1,char t2,char t3){

if (n==1)

cout<<"1 "<

else

{

hanoi(n-1,t1,t3,t2);

cout<

hanoi(n-1,t2,t1,t3);

}

main(){

int n;

cout<<"Please enter the number of Hanoi:";

cin>>n;

cout<<"Answer:"<

hanoi(n,'A','B','C');

}

函数递归调用的应用与分治策略

许多算法都采用了分治策略求解,而可以说分治与递归是一对孪生兄弟,它们经常同时被应用于算法的设计中。下面讨论著名的Catalan数问题,人们在对它的研究中充分应用了分治策略。

[例4]Catalan数问题。

[问题描述]一个凸多边形,通过不相交于n边形内部的对角线,剖分为若干个三角形。求不同的剖分方案总数H(n)。H(n)即为Catalan数。例如,n=5时H(5)=5。

[分析]Catalan数问题有着明显的递归子问题特征。在计算Catalan数时虽然可以推导出只关于n的一般公式,但在推导过程中却要用到递归公式。下面讨论三种不同的解法,其中第三种解法没有使用递归,它是由前两种解法推导而出的。

[解法1]对于多边形V1V2…Vn,对角线V1Vi(i=3,4,…,n-1)将其分为两部分,一部分是i 边形,另一部分是n-i+1边形。因此,以对角线V1Vi为一个剖分方案的剖分方案数为H(i)*H(n-i+1)。还有一种的特殊情形,是对角线V2Vn将其分为一个三角形V1V2Vn和一个n-2+1边形。为了让它同样符合粗体字给出的公式,规定H(2)=1。于是得到公式:

H(n)=∑H(i)*H(n-i+1) (i=2,3,…,n-1) ----公式(1)

H(2)=1

有了这个递归关系式,就可以用递推法或递归法解出H(n)。

[解法2]从V1向除了V2和Vn外的n-3个顶点可作n-3条对角线。每一条对角线V1Vi把多

边形剖分成两部分,剖分方案数为H(i)*H(n-i+2),由于Vi可以是V3V4…Vn-1中的任一点,且V1可换成V2,V3,…,Vn中任一点也有同样的结果。考虑到同一条对角线在2个顶点被重复计算了一次,于是对每个由顶点和对角线确定的剖分方案都乘以1/2,故有

H(n)=n∑(1/2)H(i)*H(n-i+2) (i=3,4,…,n-1)

把(1/2)提到∑外面,

H(n)=n/(2*(n-3))∑H(i)*H(n-i+2) (i=3,4,…,n-1) ----公式(2)

规定H(2)=H(3)=1,这是合理的。

由公式(2)和H(2)=1,同样可以用递推法或递归法解出H(n)。

[解法3] 把公式(1)中的自变量改为n+1,再将刚刚得出的公式(2)代入公式(1),得到

H(n+1)=∑H(i)*H(n-i+2) (i=2,3,…,n)由公式(1)

H(n+1)=2*H(n)+∑H(i)*H(n-i+2) (i=3,4,…,n-1)由H(2)=1

H(n+1)=(4n-6)/n*H(n) 由公式(2)

H(n)=(4n-10)/(n-1)*H(n-1) ----公式(3)

这是一个较之前两种解法更为简单的递归公式,还可以继续简化为

H(n)=1/(n-1)*C(n-2,2n-4) ----公式(4)

这就不需要再使用递归算法了。然而在程序设计上,公式(4)反而显得更加复杂,因为要计算阶乘。因此最后给出由公式(3)作为理论依据范例程序代码。代码相当简单,这都归功于刚才的推导。如果用前两种解法中的递归关系,程序会变得复杂且容易写错。因此,有时对具体问题将递归关系公式进行必要的化简也是至关重要的。

[代码]

//ex4.cpp

#include

#define MAXN 100

long f(int x){

if (x==3)

return(1);

else

return((4*x-10)*f(x-1)/(x-1));

}

main(){

int n;

cout<<"\nPlease input N for a Catalan number:";

cin>>n;

if ( (n<=MAXN) && (n>=3) )

cout<<"The answer is:"<

}

本例编程时还有一个细节问题需要注意。注意函数f中的斜体部分,按照公式(4)计算时一定要先进行乘法再进行除法运算,因为(4*x-10)并不总能整除(x-1),如果先进行除法则除出的小数部分将自动被舍去,从而导致得到不正确的解。

数学上许多有重要意义的计数问题都可以归结为对Catalan数的研究。可以看到,本例中的递归关系经简化还是相当简单的。下面讨论一个递归关系略为复杂的例子。

[例5]快速排序问题。

快速排序是程序设计中经常涉及的一种排序算法。它的最好时间复杂度为O(nlog2n),最差为O(n2),是一种不稳定的排序方法(大小相同的数在排序后可能交换位置)。

[算法描述]快速排序的一种基本思想是:要将n个数按由小到大排列,在待排序的n个数中选取任一个数(在本例中取第一个),称为基准数,在每一次快速排序过程中设置两个指示器i和j,对基准数左边和右边的数同时从最左(i)和最右(j)开始进行扫描(i逐1递增,j逐1递减),直到找到从左边开始的某个i大于或等于基准数,从右边开始的某个j小于或等于基准数。一旦发现这样的i和j(暂且称之为一个“逆序对”),则把第i个数和第j个数交换位置,这样它们就不再是逆序对了,紧接着再将i递增1,j递减1。如此反复,在交换过有限个逆序对后,i和j将越来越靠近,最后“相遇”,即i和j指向同一个数,暂且称之为相遇数(极端情况下,如果一开始就不存在逆序对,i和j将直接“相遇”)。相遇后就保证数列中没有逆序对了(除了在上述的极端情况下基准数和自身也算构成一个逆序对,注

意粗体字给出的逆序对的定义)。继续扫描,非极端情况下,由于数列中已经没有逆序对,i 递增1(如果相遇数小于基准数)或者j递减1(如果相遇数大于基准数)后即算完成了一趟快速排序,这时第1到第j个数中的每个都保证小于或等于基准数,第i到第n个数中的每个保证大于或等于基准数。此时,递归调用函数,对第1到第j个数和第i到第n个数分别再进行一趟快速排序。如果在极端情况下,程序认为基准数和自身构成逆序对,则将基准数与自身交换(这其实没有作用)之后i递增1,j递减1(注意斜体字给出的对逆序对的处理方法),同样对第1到第j个数和第i到第n个数分别再进行一趟快速排序。

最后的问题就是确定递归边界。由于被排序的数列将不断被划分为两个至少含一个数的子列(因为在每趟排序最后进行递归调用函数时i<>j),最后子列的长度将变为1。这就是递归的边界。在程序实现是,本着“能排则排”的原则,只要第一个数小于j(或者第i个数小于最后一个数),即进行递归。

[主程序(递归函数体)]

void QuickSort(RecType R[ ],int s,int t)

{

int i=s,j=t,k;

RecType temp;

if (s

{

temp=R[s] // 用区间第1个记录作为基准

while( i!=j) //从两端向中间交替扫描,直至i=j;

{

while( j>i&&R[j].key>temp.key)

j--;

if(i

{

R[i]=R[j];

i++;

}

while( i

i++;

if(i

{

R[j]=R[i];

j--;

}

}

R[i]=temp;

QuickSort(R,s,i-1);

QuickSort(R,i+1,t);

}

}

[例6]“九宫阵”智力游戏。

[问题描述]一个9×9方阵,由9个“九宫格”组成,每个九宫格又由3×3共9个小格子组成。请在每个空白小格子里面填上1~9的数字,使每个数字在每个九宫格内以及在整个九宫阵中的每行、每列上均出现一次。

(1)编程将下面图中的九宫阵补充完整。

(2)讨论是否可能给出“九宫阵”的全部解?

[分析]本题可利用回溯法解决,其基本思想为深度优先搜索(DFS),这也是一种以分治策略为基础的算法。回溯法与纯粹的DFS不同的是,它在搜索过程中不断杀死不合题意的结点。这一点保证了解法的效率。

首先考虑如何得出全部解的情况。

解空间树容易构造,只需按顺序(从第一行第一个数字开始到第一行最后一个,然后第二行……,一直到最后一行最后一个数字)“尝试”填入数字即可。

为了解决这个问题,我们需要先编写一个函数check,其原型为int check(int i,int j,int k),用于求第i行第j列能否填上数字k。如果可以,返回1,否则返回0。由于我们是按顺序填入数字的,看起来一个数字后面的数字并不在判断能否填的范围内。但为了解决题中某个特解问题的方便,还是引入较为严谨的判断方法。

函数check代码如下:

int check(int i,int j,int k){

int l,m,pi,pj;

//1. Check the line

for (l=1;l<=9;l++)

if ( (l!=j) && (a[i][l]!=0) && (a[i][l]==k) )

return(0);

//2. Check the column

for (l=1;l<=9;l++)

if ( (l!=i) && (a[l][j]!=0) && (a[l][j]==k) )

return(0);

//3. Check the 3x3 matrix

//3.1 Firstly we will have to check the parent_i(pi) and parent_j(pj)

if (i<=3) pi=1;

else if (i<=6) pi=4;

else pi=7;

if (j<=3) pj=1;

else if (j<=6) pj=4;

else pj=7;

//3.2 Now we can check it

for (l=0;l<=2;l++)

for (m=0;m<=2;m++){

if ( ((pi+l)!=i) && ((pj+m)!=j) )

if ( ( a[pi+l][pj+m]!=0 ) && ( a[pi+l][pj+m]==k ) )

return(0);

}

return(1);

}

结合注释很容易就能接受函数的思想,不予过多说明。

下面考虑程序最重要的部分,即递归函数。思路是这样的:假设某一格能填入某数,把这个格子看成解空间树的一个结点,由它可以扩展出9个儿子,即下一格填什么数(由1到9逐个尝试)。对下一格,同样这样考虑。不断用函数check函数考察某一个能否填入某数,一旦函数check返回0,则杀死这个结点。

如果能一直填到最后一个数,结点仍未被杀死,则这是一个解。

这种思想可用伪代码表示如下:

procedure backtrack(i,j,k:integer);

if check(i,j,k)=true then

begin

a[i,j]=k;

Generate_next_i_and_j;

if i<10 then

begin

for l:=1 to 9 do

backtrack(i,j,l);

end

else

Do_Output;

a[i,j]:=0;

end;

注意斜体的“a[i,j]:=0”必不可少!当对某个结点(x,y)扩展的过程中,可能在扩展到(x+m,y+n)时它的子树被完全杀死(每个结点都被杀死,亦即按照(x,y)及之前的填数方案填数,无解)。这时需要保证(x,y)以后所填的数被重新置零,这个语句的作用即在每个结点被杀死时都将其置零。

将伪代码翻译为C++代码:

backtrack(int i,int j,int k)

{

int l;

if (check(i,j,k)==1)

{

a[i][j]=k; //Fill in the okay solution

//Generate next i,j

if (j<9) j++;

else { i++; j=1; } //End of Generate next i,j

if (i<10)

{

for (l=1;l<=9;l++)

backtrack(i,j,l);

}

else

output();

a[i][j]=0; /*When fails and goes upperwards, the value must be cleared*/

}

}

函数output()用双重循环完成输出。在主函数main()对backtrack(1,1,i)进行一个循环,i 从1取到9,即可完成整个程序。运行时发现九宫格的解相当多,即使保存到文件中也不现实。这就回答了第2个问题。

对于第1个问题,将这个程序略加改动,即赋予全局数组a以初值,并在过程backtrack中产生下一个i和j时跳过有初值的部分,即可将程序转化为求填有部分空缺的九宫格程序。

最后给出填充有部分空缺的九宫格的完整源代码。

#include

using namespace std;

int a[11][11]={0};

int check(int i,int j,int k){

int l,m,pi,pj;

//1. Check the line

for (l=1;l<=9;l++)

if ( (l!=j) && (a[i][l]!=0) && (a[i][l]==k) )

return(0);

//2. Check the column

for (l=1;l<=9;l++)

if ( (l!=i) && (a[l][j]!=0) && (a[l][j]==k) )

return(0);

//3. Check the 3x3 matrix

//3.1 Firstly we will have to check the parent_i(pi) and parent_j(pj)

if (i<=3) pi=1;

else if (i<=6) pi=4;

else pi=7;

if (j<=3) pj=1;

else if (j<=6) pj=4;

else pj=7;

//3.2 Now we can check it

for (l=0;l<=2;l++)

for (m=0;m<=2;m++){

if ( ((pi+l)!=i) && ((pj+m)!=j) )

if ( ( a[pi+l][pj+m]!=0 ) && ( a[pi+l][pj+m]==k ) )

return(0);

}

return(1);

}

void output(){

int i,j;

cout<<"One solution is:"<

for (i=1;i<=9;i++)

{

for (j=1;j<=9;j++)

cout<

cout<

}

}

void backtrack(int i,int j,int k){

int l;

if (check(i,j,k)==1)

{

a[i][j]=k; //Fill in the okay solution

//Generate next i,j

do{

if (j<9) j++;

else { i++; j=1; }

} while (a[i][j]!=0); //End of Generate next i,j

if (i<10)

{

for (l=1;l<=9;l++)

backtrack(i,j,l);

}

else

output();

a[i][j]=0; /*When fails and goes upperwards, the value must be cleared*/

}

}

void init(){

a[1][2]=9; a[1][6]=4; a[1][7]=5; a[1][9]=7;

a[2][3]=3; a[2][5]=7; a[2][6]=9; a[2][7]=4;

a[3][4]=3; a[3][5]=6; a[3][8]=8; a[3][9]=9;

a[4][1]=3; a[4][4]=1;

a[5][3]=4; a[5][8]=2; a[5][9]=3;

a[6][2]=1; a[6][3]=2; a[6][6]=3;

a[7][1]=8; a[7][8]=5;

a[8][2]=6; a[8][4]=2; a[8][5]=9;

a[9][2]=2; a[9][3]=1; a[9][7]=8;

//memset(a,0,sizeof(a));

}

int main(){

int i;

for (i=1;i<=9;i++){

init();

backtrack(1,1,i);

}

system("PAUSE");

return 0;

}

递归方法在算法与数据结构中的应用无所不在,如动态规划(状态方程)、回溯法(深度优先搜索)等等,以上两例只是冰山一角。只有熟悉掌握函数递归调用的编程方法,深入理解分治策略的重要思想,才能编写出功能强大、高效简明的程序。

实验报告 分治与递归

实验报告分治与递归 中国矿业大学计算机科学与技术学院孟靖宇 一、实验目的与要求 1、熟悉C/C++语言的集成开发环境; 2、通过本实验加深对递归过程的理解 二、实验内容: 掌握递归算法的概念和基本思想,分析并掌握“整数划分”问题的递归算法。 三、实验题 任意输入一个整数,输出结果能够用递归方法实现整数的划分。 四、算法思想 对于数据n,递归计算最大加数等于x 的划分个数+最大加数不大于x-1的划分个数。最大加数x 从n 开始,逐步变小为n-1, (1) 考虑增加一个自变量:对于数据n,最大加数n1不大于m 的划分个数记作),(m n q 。则有: ???????>>=<==-+--+=1 1,1),()1,()1,(1),(1),(m n m n m n m n m m n q m n q n n q n n q m n q 五、代码实现 #include "stdafx.h" #include #include #include using namespace std; int q(intn,int m); int main(){ int n; cout<<"请输入要划分的整数:"<>n; int p=q(n,n); cout<<"正整数"<

return 0; } int q(intn,int m){ if((n<1)||(m<1)) return 0; if((n==1)||(m==1)) return 1; if(n

递归调用详解,分析递归调用的详细过程

递归调用详解,分析递归调用的详细过程 2009年05月23日星期六 22:52 一、栈 在说函数递归的时候,顺便说一下栈的概念。 栈是一个后进先出的压入(push)和弹出(pop)式数据结构。在程序运行时,系统每次向栈中压入一个对象,然后栈指针向下移动一个位置。当系统从栈中弹出一个对象时,最近进栈的对象将被弹出。然后栈指针向上移动一个位置。程序员经常利用栈这种数据结构来处理那些最适合用后进先出逻辑来描述的编程问题。这里讨论的程序中的栈在每个程序中都是存在的,它不需要程序员编写代码去维护,而是由运行是系统自动处理。所谓的系统自动维护,实际上就是编译器所产生的程序代码。尽管在源代码中看不到它们,但程序员应该对此有所了解。 再来看看程序中的栈是如何工作的。当一个函数(调用者)调用另一个函数(被调用者)时,运行时系统将把调用者的所有实参和返回地址压入到栈中,栈指针将移到合适的位置来容纳这些数据。最后进栈的是调用者的返回地址。当被调用者开始执行时,系统把被调用者的自变量压入到栈中,并把栈指针再向下移,以保证有足够的空间存储被调用者声明的所有自变量。当调用者把实参压入栈后,被调用者就在栈中以自变量的形式建立了形参。被调用者内部的其他自变量也是存放在栈中的。由于这些进栈操作,栈指针已经移动所有这些局部变量之下。但是被调用者记录了它刚开始执行时的初始栈指针,以他为参考,用正或负的偏移值来访问栈中的变量。当被调用者准备返回时,系统弹出栈中所有的自变量,这时栈指针移动了被调用者刚开始执行时的位置。接着被调用者返回,系统从栈中弹出返回地址,调用者就可以继续执行了。当调用者继续执行时,系统还将从栈中弹出调用者的实参,于是栈指针回到了调用发生前的位置。 可能刚开始学的人看不太懂上面的讲解,栈涉及到指针问题,具体可以看看一些数据结构的书。要想学好编程语言,数据结构是一定要学的。 二、递归 递归,是函数实现的一个很重要的环节,很多程序中都或多或少的使用了递归函数。递归的意思就是函数自己调用自己本身,或者在自己函数调用的下级

递归与分治

分治算法 一、分治算法 分治算法的基本思想是将一个规模为N的问题分解为K个规模较小的子问题,这些子问题相互独立且与原问题性质相同。求出子问题的解,就可得到原问题的解。 分治法解题的一般步骤: (1)分解,将要解决的问题划分成若干规模较小的同类问题; (2)求解,当子问题划分得足够小时,用较简单的方法解决; (3)合并,按原问题的要求,将子问题的解逐层合并构成原问题的解。 当我们求解某些问题时,由于这些问题要处理的数据相当多,或求解过程相当复杂,使得直接求解法在时间上相当长,或者根本无法直接求出。对于这类问题,我们往往先把它分解成几个子问题,找到求出这几个子问题的解法后,再找到合适的方法,把它们组合成求整个问题的解法。如果这些子问题还较大,难以解决,可以再把它们分成几个更小的子问题,以此类推,直至可以直接求出解为止。这就是分治策略的基本思想。下面通过实例加以说明。 【例1】[找出伪币] 给你一个装有1 6个硬币的袋子。1 6个硬币中有一个是伪造的,并且那个伪造的硬币比真的硬币要轻一些。你的任务是找出这个伪造的硬币。为了帮助你完成这一任务,将提供一台可用来比较两组硬币重量的仪器,利用这台仪器,可以知道两组硬币的重量是否相同。比较硬币1与硬币2的重量。假如硬币1比硬币2轻,则硬币1是伪造的;假如硬币2比硬币1轻,则硬币2是伪造的。这样就完成了任务。假如两硬币重量相等,则比较硬币3和硬币4。同样,假如有一个硬币轻一些,则寻找伪币的任务完成。假如两硬币重量相等,则继续比较硬币5和硬币6。按照这种方式,可以最多通过8次比较来判断伪币的存在并找出这一伪币。 另外一种方法就是利用分而治之方法。假如把1 6硬币的例子看成一个大的问题。第一步,把这一问题分成两个小问题。随机选择8个硬币作为第一组称为A组,剩下的8个硬币作为第二组称为B组。这样,就把1 6个硬币的问题分成两个8硬币的问题来解决。第二步,判断A和B组中是否有伪币。可以利用仪器来比较A组硬币和B组硬币的重量。假如两组硬币重量相等,则可以判断伪币不存在。假如两组硬币重量不相等,则存在伪币,并且可以判断它位于较轻的那一组硬币中。最后,在第三步中,用第二步的结果得出原先1 6个硬币问题的答案。若仅仅判断硬币是否存在,则第三步非常简单。无论A组还是B组中有伪币,都可以推断这1 6个硬币中存在伪币。因此,仅仅通过一次重量的比较,就可以判断伪币是否存在。

算法分析实验报告--分治策略

《算法设计与分析》实验报告 分治策略 姓名:XXX 专业班级:XXX 学号:XXX 指导教师:XXX 完成日期:XXX

一、试验名称:分治策略 (1)写出源程序,并编译运行 (2)详细记录程序调试及运行结果 二、实验目的 (1)了解分治策略算法思想 (2)掌握快速排序、归并排序算法 (3)了解其他分治问题典型算法 三、实验内容 (1)编写一个简单的程序,实现归并排序。 (2)编写一段程序,实现快速排序。 (3)编写程序实现循环赛日程表。设有n=2k个运动员要进行网球循环赛。现 要设计一个满足以下要求的比赛日程表:(1)每个选手必须与其它n-1个选手各赛一次(2)每个选手一天只能赛一场(3)循环赛进行n-1天 四、算法思想分析 (1)编写一个简单的程序,实现归并排序。 将待排序元素分成大小大致相同的2个子集合,分别对2个子集合进行 排序,最终将排好序的子集合合并成为所要求的排好序的集合。 (2)编写一段程序,实现快速排序。 通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有 数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数 据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据 变成有序序列。 (3)编写程序实现循环日赛表。 按分治策略,将所有的选手分为两组,n个选手的比赛日程表就可以通

过为n/2个选手设计的比赛日程表来决定。递归地用对选手进行分割, 直到只剩下2个选手时,比赛日程表的制定就变得很简单。这时只要让 这2个选手进行比赛就可以了。 五、算法源代码及用户程序 (1)编写一个简单的程序,实现归并排序。 #include #include #define MAX 10 using namespace std; void merge(int array[],int p,int q,int r) { int i,k; int begin1,end1,begin2,end2; int* temp = new int[r-p+1]; begin1 = p; end1 = q; begin2 = q+1; end2 = r; k = 0; while((begin1 <= end1)&&(begin2 <= end2)) { if(array[begin1] < array[begin2]) { temp[k] = array[begin1]; begin1++; } else { temp[k] = array[begin2]; begin2++; } k++; } while(begin1 <= end1) {

递归与分治实验报告

递归与分治实验报告 班级:计科1102 姓名:赵春晓学号:2011310200631 实验目的:进一步掌握递归与分治算法的设计思想,通过实际问题来应用递归与分治设计算法。 实际问题:1集合划分问题,2输油管道问题,3邮局选址问题,4整数因子分解问题,5众数问题。 问题1:集合划分 算法思想:对于n个元素的集合,可以划分为由m个子集构成的集合,例如{{1,2}{3,4}}就是由2个子集构成的非空子集。假设f(n,m)表示将n个元素划分成由m个子集构成的集合的个数。那么1)若m == 1 ,则f(n,m)= 1 ;2)若n == m ,则f(n,m)= 1 ;3)若不是上面两种情况则有下面两种情况构成:3.1)向n-1个元素划分成的m个集合里面添加一个新的元素,则有m*f(n-1,m)种方法;3.2)向n-1个元素划分成的m-1个集合里添加一个由一个元素形成的独立的集合,则有f(n-1,m-1)种方法。 实验代码: #include #include using namespace std ; int jihehuafen( int n , int m ) { if( m == 1 || n == m ) return 1 ; else return jihehuafen( n - 1 , m - 1 ) + m*jihehuafen( n - 1 , m ) ; } int main() { ifstream fin("C:/input.txt") ; ofstream fout("C:/output.txt") ; int N , M , num ; fin >> N >> M ; num = jihehuafen( N , M) ; fout << num << endl ; return 0 ; } 问题2:输油管道 算法思想:由于主管道由东向西铺设。故主管道的铺设位置只和各油井的y坐标有关。要使主管道的y坐标最小,主管道的位置y坐标应是各个油井y坐标的中位数。先用快速排序法把各个油井的y坐标排序,然后取其中位数再计算各个油

函数的递归调用与分治策略

函数的递归调用与分治策 略 This manuscript was revised on November 28, 2020

函数的递归调用与分治策略 递归方法是算法和程序设计中的一种重要技术。递归方法即通过函数或过程调用自身将问题转化为本质相同但规模较小的子问题。递归方法具有易于描述和理解、证明简单等优点,在动态规划、贪心算法、回溯法等诸多算法中都有着极为广泛的应用,是许多复杂算法的基础。递归方法中所使用的“分而治之”的策略也称分治策略。 递归方法的构造 构造递归方法的关键在于建立递归关系。这里的递归关系可以是递归描述的,也可以是递推描述的。下面由一个求n的阶乘的程序为例,总结出构造递归方法的一般步骤。 [例1]从键盘输入正整数N(0<=N<=20),输出N!。 [分析]N!的计算是一个典型的递归问题。使用递归方法来描述程序,十分简单且易于理解。 [步骤1]描述递归关系递归关系是这样的一种关系。设{U1,U2,U3,…,Un…}是一个序列,如果从某一项k开始,Un和它之前的若干项之间存在一种只与n有关的关系,这便称为递归关系。 注意到,当N>=1时,N!=N*(N-1)!(N=1时,0!=1),这就是一种递归关系。对于特定的K!,它只与K与(K-1)!有关。 [步骤2]确定递归边界在步骤1的递归关系中,对大于k的Un的求解将最终归结为对Uk的求解。这里的Uk称为递归边界(或递归出口)。在本例中,递归边界为k=0,即0!=1。对于任意给定的N!,程序将最终求解到0!。 确定递归边界十分重要,如果没有确定递归边界,将导致程序无限递归而引起死

循环。例如以下程序: #include <> int f(int x){ return(f(x-1)); } main(){ cout<=1时 n!= 1 当N=0时 再将这种关系翻译为代码,即一个函数: long f(int n){ if (n==0) return(1); else return(n*f(n-1)); } [步骤4]完善程序主要的递归函数已经完成,将程序依题意补充完整即可。

算法分析与设计实验一递归与分治策略

实验一递归与分治策略 实验目的 1.了解并掌握递归的概念,递归算法的基本思想; 2.掌握分治法的基本思想方法; 3.了解适用于用分治法求解的问题类型,并能用递归或非递归的方式设计相应的分治法算法; 4.掌握分治法复杂性分析方法,比较同一个问题的递归算法与循环迭代算法的效率。预习与实验要求 1.预习实验指导书及教材的有关内容,掌握分治法的基本思想; 2.严格按照实验内容进行实验,培养良好的算法设计和编程的习惯; 3.认真听讲,服从安排,独立思考并完成实验。 实验原理 简单说来,当一个函数用它自己来定义时就称为递归。一般来说,递归需要有边界条件、递归前进段和递归返回段。当边界条件不满足时,递归前进;当边界条件满足时,递归返回。因此,在考虑使用递归算法编写程序时,应满足两点:1)该问题能够被递归形式描述;2)存在递归结束的边界条件。递归的能力在于用有限的语句来定义对象的无限集合。用递归思想写出的程序往往十分简洁易懂。一般说来,一个递归算法可以转换称为一个与之等效的非递归算法,但转换后的非递归算法代码将成倍地增加。 分治是一种被广泛应用的有效方法,它的基本思想是把最初的问题分解成若干子问题,然后在逐个解决各个子问题的基础上得到原始问题的解。所谓分治就是“分而治之”的意思。由于分解出的每个子问题总是要比最初的问题容易些,因而分治策略往往能够降低原始问题的难度,或者提高解决原始问题的效率。 根据如何由分解出的子问题求出原始问题的解,分治策略又可分为两种情形:第一,原始问题的解只存在于分解出的某一个子问题中,则只需要在原始问题的一个划分中求解即可;第二,原始问题的解需要由各个子问题的解再经过综合处理得到。无论是哪一种情况,分治策略可以较快地缩小问题的求解范围,从而加快问题求解的速度。 分治策略运用于计算机算法是,往往会出现分解出来的子问题与原始问题类型相同的现象,而与原问题相比,各个子问题的规模变小了,这刚好符合递归的特征。因此分治策略往往是和递归联系在一起的。

算法设计与分析:递归与分治法-实验报告

应用数学学院信息安全专业班学号姓名 实验题目递归与分治法 综合实验评分表

实验报告 一、实验目的与要求 1.掌握递归算法的设计思想 2.掌握分治法设计算法的一般过程 3.理解并掌握算法渐近时间复杂度的分析方法 二、实验内容 1、折半查找的递归算法 (1)源程序代码 #include #include using namespace std; int bin_search(int key[],int low, int high,int k) { int mid; if(low>high) return -1; else{ mid = (low+high) / 2; if(key[mid]==k) return mid; if(k>key[mid]) return bin_search(key,mid+1,high,k); else return bin_search(key,low,mid-1,k); } } int main() { int n , i , addr; int A[10] = {2,3,5,7,8,10,12,15,19,21}; cout << "在下面的10个整数中进行查找" << endl; for(i=0;i<10;i++){ cout << A[i] << " " ; } cout << endl << endl << "请输入一个要查找的整数" << endl; cin >> n; addr = bin_search(A,0,9,n);

if(-1 != addr) cout << endl << n << "是上述整数中的第" << addr << "个数" << endl; else cout << endl << n << "不在上述的整数中" << endl << endl; getchar(); return 0; } (2)运行界面 ①查找成功 ②查找失败

实验7-2-函数调用

实验7-2 函数(二) 1 【实验目的】 (1)掌握函数的嵌套调用的方法 (2)掌握函数的递归调用的方法 (3)掌握全局变量和局部变量的概念和用法 【实验要求】 (1)熟练掌握函数的嵌套调用的方法 (2)熟练掌握函数的递归调用的方法 【实验环境】 (1) Microsoft XP操作系统 (2) Microsoft VC++ 6.0 【实验内容】 1、素数https://www.doczj.com/doc/c66056084.html,/acmhome/problemdetail.do?&method=showdetail&id=1098描述:输出100->200之间的素数的个数,以及所有的素数。 输入:无 输出:100->200之间的素数的个数,以及所有的素数。 样例输入:无 样例输出:

21 101 103 ... 197 199 2、字符串逆序https://www.doczj.com/doc/c66056084.html,/JudgeOnline/problem.php?id=1499 题目描述:写一函数,使输入的一个字符串按反序存放,在主函数中输入输出反序后的字符串。 输入:一行字符 输出:逆序后的字符串 样例输入:123456abcdef 样例输出:fedcba654321 3、字符串拼接https://www.doczj.com/doc/c66056084.html,/JudgeOnline/problem.php?id=1500 题目描述:写一函数,将两个字符串连接 输入:两行字符串 输出:链接后的字符串 样例输入: 123 abc 样例输出 123abc 4、输出元音https://www.doczj.com/doc/c66056084.html,/JudgeOnline/problem.php?id=1501

使用分治策略递归和非递归和递推算法解决循环赛日程表课程设计报告

《算法设计与分析》 课程设计报告 题目:循环赛日程表 院(系):信息科学与工程学院 专业班级:软工 学生姓名: 学号: 指导教师: 2018 年 1 月 8 日至 2018 年 1 月 19 日

算法设计与分析课程设计任务书

目录 1 常用算法 (1) 1.1分治算法 (1) 基本概念: (1) 1.2递推算法 (2) 2 问题分析及算法设计 (5) 2.1分治策略递归算法的设计 (5) 2.2 分治策略非递归算法的设计 (7) 2.3 递推策略算法的设计 (8) 3 算法实现 (9) 3.1分治策略递归算法的实现 (9) 3.2 分治策略非递归算法的实现 (10) 3.3 递推策略算法的实现 (12) 4 测试和分析 (15) 4.1分治策略递归算法测试 (15) 4.2分治策略递归算法时间复杂度的分析 (16) 4.3 分治策略非递归算法测试 (16) 4.4分治策略非递归算法时间复杂度的分析 (17) 时间复杂度为:O(5^(n-1)) (17) 4.5 递推策略算法测试 (17) 4.6 递推策略算法时间复杂度的分析 (18) 时间复杂度为:O(5^(n-1)) (18) 4.7 三种算法的比较 (18) 5 总结 (19) 参考文献 (20)

1 常用算法 1.1分治算法 基本概念: 在计算机科学中,分治法是一种很重要的算法。字面上的解释是“分而治之”,就是把一个复杂的问题分成两个或更多的相同或相似的子问题,再把子问题分成更小的子问题……直到最后子问题可以简单的直接求解,原问题的解即子问题的解的合并。这个技巧是很多高效算法的基础,如排序算法(快速排序,归并排序),傅立叶变换(快速傅立叶变换)…… 任何一个可以用计算机求解的问题所需的计算时间都与其规模有关。问题的规模越小,越容易直接求解,解题所需的计算时间也越少。例如,对于n个元素的排序问题,当n=1时,不需任何计算。n=2时,只要作一次比较即可排好序。n=3时只要作3次比较即可,…。而当n较大时,问题就不那么容易处理了。要想直接解决一个规模较大的问题,有时是相当困难的。 基本思想及策略: 分治法的设计思想是:将一个难以直接解决的大问题,分割成一些规模较小的相同问题,以便各个击破,分而治之。 分治策略是:对于一个规模为n的问题,若该问题可以容易地解决(比如说规模n较小)则直接解决,否则将其分解为k个规模较小的子问题,这些子问题互相独立且与原问题形式相同,递归地解这些子问题,然后将各子问题的解合并得到原问题的解。这种算法设计策略叫做分治法。 如果原问题可分割成k个子问题,1

分治算法实验(用分治法实现快速排序算法)

算法分析与设计实验报告第四次附加实验

while (a[--j]>x); if (i>=j) { break; } Swap(a[i],a[j]); } a[p] = a[j]; //将基准元素放在合适的位置 a[j] = x; return j; } //通过RandomizedPartition函数来产生随机的划分 template vclass Type> int RandomizedPartition(Type a[], int p, int r) { int i = Random(p,r); Swap(a[i],a[p]); return Partition(a,p,r); } 较小个数排序序列的结果: 测试结果 较大个数排序序列的结果:

实验心得 快速排序在之前的数据结构中也是学过的,在几大排序算法中,快速排序和归并排序尤其是 重中之重,之前的快速排序都是给定确定的轴值,所以存在一些极端的情况使得时间复杂度 很高,排序的效果并不是很好,现在学习的一种利用随机化的快速排序算法,通过随机的确 定轴值,从而可以期望划分是较对称 的,减少了出现极端情况的次数,使得排序的效率挺高了很多, 化算法想呼应,而且关键的是对于随机生成函数,通过这一次的 学习终于弄明白是怎么回事了,不错。 与后面的随机实 验和自己的 实验得分助教签名 附录: 完整代码(分治法) //随机后标记元素后的快速排序 #i nclude #in elude #inelude #include using namespacestd; template < class Type> void S &x,Type &y); // 声明swap函数 inline int Random(int x, int y); // 声明内联函数 template < class Type> int Partition(Type a[], int p, int r); // 声明 Partition 函数template int RandomizedPartition(Type a[], int p, int r); // 声明 RandomizedPartition 函数 int a[1000000]; //定义全局变量用来存放要查找的数组 更大个数排序序列的结果:

递归算法详解

递归算法详解 C通过运行时堆栈支持递归函数的实现。递归函数就是直接或间接调用自身的函数。 许多教科书都把计算机阶乘和菲波那契数列用来说明递归,非常不幸我们可爱的著名的老潭老师的《C语言程序设计》一书中就是从阶乘的计算开始的函数递归。导致读过这本经书的同学们,看到阶乘计算第一个想法就是递归。但是在阶乘的计算里,递归并没有提供任何优越之处。在菲波那契数列中,它的效率更是低的非常恐怖。 这里有一个简单的程序,可用于说明递归。程序的目的是把一个整数从二进制形式转换为可打印的字符形式。例如:给出一个值4267,我们需要依次产生字符‘4’,‘2’,‘6’,和‘7’。就如在printf函数中使用了%d格式码,它就会执行类似处理。 我们采用的策略是把这个值反复除以10,并打印各个余数。例如,4267除10的余数是7,但是我们不能直接打印这个余数。我们需要打印的是机器字符集中表示数字‘7’的值。在ASCII码中,字符‘7’的值是55,所以我们需要在余数上加上48来获得正确的字符,但是,使用字符常量而不是整型常量可以提高程序的可移植性。‘0’的ASCII码是48,所以我们用余数加上‘0’,所以有下面的关系: ‘0’+ 0 =‘0’ ‘0’+ 1 =‘1’ ‘0’+ 2 =‘2’ ... 从这些关系中,我们很容易看出在余数上加上‘0’就可以产生对应字符的代码。接着就打印出余数。下一步再取商的值,4267/10等于426。然后用这个值重复上述步骤。 这种处理方法存在的唯一问题是它产生的数字次序正好相反,它们是逆向打印的。所以在我们的程序中使用递归来修正这个问题。 我们这个程序中的函数是递归性质的,因为它包含了一个对自身的调用。乍一看,函数似乎永远不会终止。当函数调用时,它将调用自身,第2次调用还将调用自身,以此类推,似乎永远调用下去。这也是我们在刚接触递归时最想不明白的事情。但是,事实上并不会出现这种情况。 这个程序的递归实现了某种类型的螺旋状while循环。while循环在循环体每次执行时必须取得某种进展,逐步迫近循环终止条件。递归函数也是如此,它在每次递归调用后必须越来越接近某种限制条件。当递归函数符合这个限制条件时,它便不在调用自身。 在程序中,递归函数的限制条件就是变量quotient为零。在每次递归调用之前,我们都把quotient除以10,所以每递归调用一次,它的值就越来越接近零。当它最终变成零时,递归便告终止。 /*接受一个整型值(无符号0,把它转换为字符并打印它,前导零被删除*/

【习题】函数调用Word版

函数调用 【实验目的】: 1. 掌握函数的定义和调用方法。 2. 练习重载函数的使用。 3. 练习有默认参数值的函数的使用。 4. 练习使用系统函数。 5. 熟悉多文件工程结构。 【实验内容】: 1.编写函数int add(int x, int y),实现两个整型数据x,y的求和功能。 ·要求:使用Visual C++的Debug调试功能,记录在函数调用时实参和形参的值 的变化。 2.编写一个求x的n次方的程序int pow(int m, int n),计算m的n次方的结果。 3.利用上题中设计两个函数,设计一个求两个整数的平方和的程序。要求如下: a)主函数中调用求和函数: int add(int x, int y);

求和函数add中调用上题设计的int pow(int m, int n)函数来计算其平方。 4.多文件程序结构:一个文件可以包含多个函数定义,但是一个函数的定义必须完 整的存在于一个文件中。要求: a)将add函数的声明部分放在头文件(add.h)中,实现部分放在源文件(add.cpp) 中。 b)将pow函数的声明部分放在头文件(pow.h)中,实现部分放在源文件(pow.cpp) 中。 c)在main函数中调用add函数,计算从屏幕终端输入的两个数据之和。(main 函数的实现在main.cpp中) 5.将第2题设计的pow函数修改成为递归函数。

6.设计一个函数int fac(int n),利用函数的递归调用,来计算n!(n的阶乘)。 ·要求:单步调试程序,记录递归函数的调用过程。 7.使用系统函数pow(x,y)计算x y的值,注意包含头文件cmath。 8.从键盘输入两个数字,分别赋值给变量a、b,设计一个子函数swap,实现这两个数字交换次序。(注:根据需要自己设计函数的参数及返回值) ·要求:使用Visual C++的Debug调试功能,记录在函数调用时实参和形参的值的变化。 9.设计一个函数,求圆的面积。 要求:在主函数中调用子函数calArea计算圆的面积。并将calArea函数设计为内联函数。

算法分析实验报告--分治策略

分治策略 姓名:XXX 专业班级:XXX 学号:XXX 指导教师:XXX 完成日期:XXX

一、试验名称:分治策略 (1)写出源程序,并编译运行 (2)详细记录程序调试及运行结果 二、实验目的 (1)了解分治策略算法思想 (2)掌握快速排序、归并排序算法 (3)了解其他分治问题典型算法 三、实验内容 (1)编写一个简单的程序,实现归并排序。 (2)编写一段程序,实现快速排序。 (3)编写程序实现循环赛日程表。设有n=2k个运动员要进行网球循环赛。现 要设计一个满足以下要求的比赛日程表:(1)每个选手必须与其它n-1个选手各赛一次(2)每个选手一天只能赛一场(3)循环赛进行n-1天 四、算法思想分析 (1)编写一个简单的程序,实现归并排序。 将待排序元素分成大小大致相同的2个子集合,分别对2个子集合进行 排序,最终将排好序的子集合合并成为所要求的排好序的集合。 (2)编写一段程序,实现快速排序。 通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有 数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数 据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据 变成有序序列。 (3)编写程序实现循环日赛表。 按分治策略,将所有的选手分为两组,n个选手的比赛日程表就可以通 过为n/2个选手设计的比赛日程表来决定。递归地用对选手进行分割, 直到只剩下2个选手时,比赛日程表的制定就变得很简单。这时只要让

这2个选手进行比赛就可以了。 五、算法源代码及用户程序 (1)编写一个简单的程序,实现归并排序。 #include #include<> #define MAX 10 using namespace std; void merge(int array[],int p,int q,int r) { int i,k; int begin1,end1,begin2,end2; int* temp = new int[r-p+1]; begin1 = p; end1 = q; begin2 = q+1; end2 = r; k = 0; while((begin1 <= end1)&&(begin2 <= end2)) { if(array[begin1] < array[begin2]) { temp[k] = array[begin1]; begin1++; } else { temp[k] = array[begin2]; begin2++; } k++; } while(begin1 <= end1) { temp[k++] = array[begin1++]; }

实验一分治与递归算法报告

实验一分治与递归算法的应用一、实验目的 1.掌握分治算法的基本思想(分-治-合)、技巧和效率分析方法。 2.熟练掌握用递归设计分治算法的基本步骤(基准与递归方程)。 3.学会利用分治算法解决实际问题。 二、算法问题要求 老板有一袋金块(共n块,n是2的幂(n≥2)),最优秀的雇员得到其中最重的一块,最差的雇员得到其中最轻的一块。假设有一台比较重量的仪器,希望用最少的比较次数找出最重和最轻的金块。并对自己的程序进行复杂性分析。 三、算法设计 在n个元素的集合中寻找最大和最小值元素。(1)将集合一分为二,变为两个集合,目的是在较小的两个集合中分别找最大、最小元素。(2)递归分解较小集合,直到每个集合中的元素个数≤2,然后找出小集合的最大、最小元素。(3)合并(回溯):自低向上把子问题的解合并,大元素中取最大者,小元素中取最小者,最后得到元问题的解。

四、验证分析实验结果 图1.1 运行界面 图1.2 运行结果五、程序 #include int a[100]; maxmin(int i,int j,int &fmax,int &fmin){ int mid ; int lmin,lmax,rmin,rmax; if (i==j){

fmax=a[i]; fmin=a[i]; } else if (i==(j-1)){ if (a[i]rmax) fmax=lmax; else fmax=rmax; if ( lmin

实验一 递归与分治策略算法设计与实现实验报告

华北水利水电学院算法分析与设计实验报告 20010~2011学年第二学期2008级计算机科学与技术专业 班级:2008109 学号:200810906 姓名:刘景超 实验一递归与分治算法的设计与实现 一、实验目的: 1、了解递归、分治算法的设计思路与设计技巧,理解递归的概念,掌握设计有效算法的 分治策略。 2、通过实际案例,领会算法的执行效率 二、试验内容: 棋盘覆盖、最接近点对、排序算法、矩阵乘法等,(也可选作其它问题); 三、核心程序源代码: #include #include void main() { void hanoi(int n,char one,char two,char three); int m; cout<<"请输入要移动的盘子的数目:"<>m; cout<<"盘子的数目为:"<"<

五、小结 本想用MFC采用图形的方式展示移动的过程,可惜水平有限,实在是写不出来,只好采用控制台程序了。采用控制台程序表述还是很简单的,算法也不复杂。这次实验让我认识到我在MFC方面基础还很薄弱,还需要多多练习,慢慢提升自己。

实验1++递归与分治算法

淮海工学院计算机工程学院实验报告书 课程名:《算法分析与设计》 题目:实验1 递归与分治算法 班级: 学号: 姓名:

实验1 递归与分治算法 实验目的和要求 (1)进一步掌握递归算法的设计思想以及递归程序的调试技术; (2)理解这样一个观点:分治与递归经常同时应用在算法设计之中。 (3)分别用蛮力法和分治法求解最近对问题; (4)分析算法的时间性能,设计实验程序验证分析结论。 实验内容 设p1=(x1, y1), p2=(x2, y2), …, pn=(xn, yn)是平面上n个点构成的集合S,设计算法找出集合S中距离最近的点对。 实验环境 Turbo C 或VC++ 实验学时 2学时,必做实验 数据结构与算法 核心源代码 蛮力法: #include #include #include int ClosestPoints(int x[ ], int y[ ], int n); int main() { int x[3],y[3]; printf("请输入各点的横坐标: "); for(int i=0;i<4;i++) { scanf("%d",&x[i]); } printf("请输入各点的纵坐标: "); for(int j=0;j<4;j++)

{ scanf("%d",&y[i]); } ClosestPoints(x,y,4); return 0; } int ClosestPoints(int x[ ], int y[ ], int n) { int index1, index2; //记载最近点对的下标 int d, minDist = 1000; //假设最大距离不超过1000 for (int i = 0; i < n - 1; i++) for (int j = i + 1; j < n; j++) //只考虑i<j的点对 { d =sqrt ((x[i]-x[j])* (x[i]-x[j]) + (y[i]-y[j])* (y[i]-y[j])); if (d < minDist) { minDist = d; index1 = i; index2 = j; } } cout<<"最近的点对是:"< #include const int n = 4; struct point //定义点的结构体 { int x, y; };

第十一讲 函数的递归调用及函数中的变量定义

第十一讲函数的递归调用及函数中的变量定义 一、函数的递归调用 1.递归的概念 直接递归调用:调用函数的过程中又调用该函数本身,自己调用自己。 间接递归调用:调用f1函数的过程中调用f2函数,而f2中又需要调用f1。 以上均为无终止递归调用。为了让这种调用终止,一般要用if语句来控制使递归过程到某一条件满足时结束。 2、递归法 类似于数学证明中的反推法,从后一结果与前一结果的关系中寻找其规律性。 递归法:从结果出发,归纳出后一结果与前一结果直到初值为止存在的关系 编程思想:设计一个函数(递归函数),这个函数不断使用下一级值调用自身,直到结果已知处——选择控制结构 其一般形式是: 递归函数名f (参数n) { if (n=初值) 结果=常量; else 结果=含f(x-1)的表达式; return 结果; } 例1.输入一个n,编写函数求n!,根据不同的算法,我们可以用三种方式。 方式一:用递推算法,Sn=n!的递推关系如下: 1 (n=1,0) Sn= Sn-1×n n>1 是一种累计乘积的关系,先得到上一个数的阶乘,然后再得到得到下个数的阶乘,用循环结构来实现。 程序代码如下: main()

{ int n; float sn; float fac(int ); /*函数的声明*/ printf("Input n="); scanf("%d",&n); sn=fac(n); /*函数的调用*/ printf("%d!=%.0f",n,sn); } float fac(int n) /*函数的定义*/ { float f=1.0; int i; if (n==0||n==1) return f; for(i=1;i<=n;i++) f=f*i; return f; } 方式二:用递归算法,f(n)=n!的递归求解关系如下: 1 (n=1,0) f(n)= f(n-1)×n n>1 递归程序分两个阶段执行—— ①回推(调用):欲求n! →先求 (n-1)! →(n-2)! →…→ 1! 若1!已知,回推结束。 ②递推(回代):知道1!→2!可求出→3!→…→ n! 注意:在此可画图来说明 程序清单如下: main() { int n; float sn; float fac(); /*函数的声明*/ clrscr(); printf("Input n=");

用递归法解决问题

3.5用递归法解决问题 【教材分析】 “用递归法解决问题”是《算法与程序设计》第三章第5节的内容,学业水平测试对本节内容也达到了B级要求,本节内容是在学习了VB基础知识中的三种基本结构,并且学习了数组、用解析法和穷举法解决问题等算法。本节先后介绍了“什么是递归法”、“自定义函数”、以及应用自定义函数结合递归算法来解决问题实例。通过本节内容的学习可以培养学生分析和分解问题的能力。从教材的结构上看“自定义函数”和“递归算法”是独立的,可以分别讲解,但在使用时两者是相辅相成的。 【学情分析】 这节课的教学对象是高中二年级学生,已经学习了算法与程序设计VB中的一些基础知识,初步了解了算法的概念。特点是在学习循环结构的过程中,学生已经积累了一些“递归”和“穷举”的算法。但是学生对函数尤其是“自定义函数”非常陌生,而“自定义函数”和“递归法”是本册的学习重点,也是以后编程的重点。学习本节内容学生可以充分体会递归算法的思想过程,扩大原来的知识面,进一步认识程序设计的功能,进一步激发学生学习算法与程序设计的兴趣。 【教学目标】 1.知识与技能: 理解什么是递归法,会用递归法的思想分析和解决问题 理解什么是自定义函数,能应用自定义函数实现递归算法的编程 2.过程与方法 学生通过思考、探究,体验递归算法和发现问题与解决问题的步骤 3.情感态度与价值观 在建立数学模型中培养学生的抽象思维能力,培养学生多维度思考问题和解决能力。 树立多学科整合的思想意识,能够用联系的观点解决问题。 【教学重点】 理解什么是递归算法,学会用递归法的思想分析问题。 理解自定义函数的概念。 【教学难点】 用自定义函数和递归算法编写程序解决问题 【教学方法及策略】

相关主题
文本预览
相关文档 最新文档