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

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

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

函数的递归调用与分治策

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<

}

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

[步骤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]完善程序主要的递归函数已经完成,将程序依题意补充完整即可。

ey>

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;

Check the line

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

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

return(0);

Check the column

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

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

return(0);

Check the 3x3 matrix

Check the line

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

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

return(0);

Check the column

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

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

return(0);

Check the 3x3 matrix

// 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;

// 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;

}

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

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