递归算是算法中比较大的一道坎,时常因为想观察内部过程而百思不得其解。
在此记录一下本人学习递归的心得,我会用递归去实现简单到复杂的题目。
首先,构造递归方程的特点:
1. 首先 需要知道递归的出口,也就是递归函数结束的条件;
2. 观察递归关系式,如果问题的解决方式都一样的话,那么大的问题就可以划分为小问题。小问题会到达递归出口,再次返回给大问题;
3. 建立递归方程
以下问题 均使用以上步骤解决
问题1 ,数组求和,已知一个数组array[ ],用递归的方法求sum( array );
1.递归出口:如果数组只有一个元素 sum( array[ ] ) = array[ 0 ];
2.递归关系:拆解为子问题,观察sum(array[ n ])与sum(array[ n-1 ])的关系,如果没有,多观察几项或者从整体观察;
3.建立递归方程:sum( array[ n ] ) = sum( array[ n - 1 ] ) + array[ n ];
int sum(int array[], int n){
if(n==0) return array[0];
else{
return (sum(array, n-1) + array[n]);
}
}
int main(){
int a[10]={1,2,3,4,5,6,7,8,9,0};
cout<<sum(a,9);
return 0;
}
out: 45
问题2 , 求数组中的最大值,已知一个数组array[ n ],求Max(array[ n ]);
- 递归出口:如果只有一个元素,最大值就是array[ 0 ];
- 递归关系:本例在数值上没有规律,但是可以从局部观察到整体:
- 如果n1;Max(array[ n ]) = array[ 0 ];
如果n2;Max(array[ n ]) = max(array[ 1 ] array[ 0 ]);
如果n3;Max(array[ n ]) = max(array[ 2 ], array[ 1 ], array[ 0 ]);
此时我们发现 Max(array[ 2 ])已经有了结果;那第三步又可以化简如下
如果n3;Max(array[ n ]) = max(Max(array[ 1 ]), array[ 0 ]);
类推n==4;Max(array[ n ]) = max(Max(array[ 2 ]), array[ 3 ]);
最终我们得到递归方程 Max(array[n]) = max(Max(array[n-2]), array[n-1]);
int GetMax(int array[], int n){
if (n == 0) return array[0];
else{
return GetMax(array, n-1) > array[n] ? GetMax(array,n-1) : array[n];
}
}
int main(){
int array[7] = {2,33,54,65,21,61,1};
int max = array[6];
cout << GetMax(array, 6);
return 0;
}
out: 65
我们熟悉的 Fibnacci 数列
1.出口 f( 1 ) = f( 2 ) = 1;
2.递归关系 第 n 项是 n -1 项 + n - 2 项
3. 递归方程 f( n ) = f( n - 1 ) + f( n - 2 );
int fibnacci(int n){
if(n == 1 || n == 2) return 1;
else return (fibnacci( n - 1 ) + fibnacci( n - 2 ));
}
int main(){
int n;
cin>>n;
cout<<fibnacci(n);
return 0;
}
以上是一维数组,二维数组同理。
如果我们想要用递归的方法打印出杨辉三角,同样按照规律
- 递归出口:如果只有一层的话,a[0][0] = 1 && i < j ;
- 递归关系:如果超过一层的话,每一项的值 a[i][j] 都等于 a[i-1][j-1] + a[i][j-1]
- 因此可以列出递归关系式 a[i][j] = a[i-1][j-1] + a[i][j-1]
int Function(int i, int j){
if (i < j) return 0;
if (i==0 || j==0) return 1;
else{
return (Function(i-1,j-1) + Function(i-1,j));
}
}
int main(){
cout<<Function(4,2)<<endl;
}
out : 6
Function(9,4) : 126
最后举一个经典的汉诺塔例子吧:
- 递归出口:当有一个盘子时,我们将直接由A —> C
- 递归关系:当有n个盘子时,我们先将前 n - 1 个盘由 A —> B 再将第 n 个盘由 A --> C 再将前 n - 1 个盘由 B–> C
为了更方便的观察
当有1个盘子时,A —> C;
当有2个盘子时,A —> B, A---->C , B---->C;
当有3个盘子时,A —> C, A---->B , C---->B, A—>C, B—>A, B—>C, A—>C;
需要注意的是 加粗的 A–>C 是将中间的第 2个盘由 A --> C ;
前面的部分A —> C, A---->B , C---->B是 将前 1 个盘由 A —> B。
后面的部分 B—>A, B—>C, A—>C是 将前 1 个盘由 B–> C
在此 我们观察前一部分 A —> C, A---->B , C---->B ,跟当有2个盘子时的 A —> B, A---->C , B---->C;我们不难发现,只是BC调换了位置。同理 我们观察‘’B—>A, B—>C, A—>C ” 与 “A —> B, A---->C , B---->C” 发现,这时是AB调换了位置。
当有4个盘子时,A–>B, A–>C, B–>C, A–>B, C–>A, C–>B, A–>B, A–>C, B–>C, B–>A, C–>A, B–>C, A–>B, A–>C, B–>C;
我们用前一部分与三个盘子比较A —> C, A---->B , C---->B, A—>C, B—>A, B—>C, A—>C 还是BC调换了位置,同理,后一部分是相同的。因此我们可以写出递归的方程
- 第n个盘子移动是在第n-1个盘子移动的基础上调换了BC顺序,移动第n个盘子之后,然后在第n-1个盘子基础上移动AB顺序;
hanoi(n - 1, a, c, b);
hanoi(1, a, b, c);
hanoi(n - 1, b, a, c);
#include <iostream>
#include <algorithm>
using namespace std;
int t, n;
void hanoi(int n, char a, char b, char c)
{
if (n == 1)
cout << "第" << ++t << "步:" << a << "-->" << c << endl;
else
{
hanoi(n - 1, a, c, b);
hanoi(1, a, b, c);
hanoi(n - 1, b, a, c);
}
}
int main()
{
cin >> n;
char a = 'A', b = 'B', c = 'C';
hanoi(n, a, b, c);
return 0;
}