递归算法

Posted 花海漂

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了递归算法相关的知识,希望对你有一定的参考价值。

一个函数调用其自身,就是递归
求n!的递归函数

1  int Factorial(int n)
2 {
3     if (n == 0)
4          return 1;
5      return  n * Factorial(n - 1);
6 }

F(3)3->F(3)5->F(2)3->F(2)5->F(1)3->F(1)5-> F(0)3->F(0)4:返回1->
F(1)5:返回1*1->F(2)5:返回2*1-> F(3)5:返回3*2-> 函数执行结束

汉诺塔问题

古代有一个梵塔,塔内有三个座A、B、C,A座上有64个盘子,盘子大小不等,大的在下,小的在上(如图)。有一个和尚想把这64个盘子从A座移到C座,但每次只能允许移动一个盘子,并且在移动过程中,3个座上的盘子始终保持大盘在下,小盘在上。在移动过程中可以利用B座,要求输出移动的步骤

 #include <iostream>
 using namespace std;
 void Hanoi(int n, char src,char mid,char dest)
 //将src座上的n个盘子,以mid座为中转,移动到dest座
 {
  if( n == 1) { //只需移动一个盘子
    cout << src << "->" << dest << endl;
     //直接将盘子从src移动到dest即可
    return ; //递归终止
   }
   Hanoi(n-1,src,dest,mid); //先将n-1个盘子从src移动到mid
   cout << src << "->" << dest << endl;
   //再将一个盘子从src移动到dest
   Hanoi(n-1,mid,src,dest); //最后将n-1个盘子从mid移动到dest
  return ;
}
//主函数
int main()
{ 
    int n; cin >> n; //输入盘子数目 
    Hanoi(n,A,B,C); 
    return 0;    
}

用递归替代多重循环

N皇后问题

输入一个正整数N,则程序输出N皇后问题的全部摆法。
输出结果里的每一行都代表一种摆法。行里的第i个数字如果是n,就代表第i行的皇后应该放在第n列。
皇后的行、列编号都是从1开始算。
样例输入:
4
样例输出:
2 4 1 3
3 1 4 2

#include <iostream>
#include <cmath>
using namespace std;
int N;
int queenPos[100];
//用来存放算好的皇后位置。最左上角是(0,0)
void NQueen( int k);
int main()
{
    cin >> N;
    NQueen(0); //从第0行开始摆皇后
    return 0;
}

void NQueen( int k) { //在0~k-1行皇后已经摆好的情况下,摆第k行及其后的皇后
    int i;
    if( k == N ) { // N 个皇后已经摆好
        for( i = 0; i < N;i ++ )
            cout << queenPos[i] + 1 << " ";
        cout << endl;
        return ;
    }
    for( i = 0;i < N;i ++ ) { //逐尝试第k个皇后的位置
        int j;
        for( j = 0; j < k; j ++ ) {
            //和已经摆好的 k 个皇后的位置比较,看是否冲突
            if( queenPos[j] == i ||abs(queenPos[j] - i) == abs(k-j)) {
                break; //冲突,则试下一个位置
            }
        }     
        if( j == k ) { //当前选的位置 i 不冲突
            queenPos[k] = i; //将第k个皇后摆放在位置 i
            NQueen(k+1);
        }
    } //for( i = 0;i < N;i ++ )
}   

逆波兰表达式

逆波兰表达式是一种把运算符前置的算术表达式,例如普通的表达式2 + 3的逆波兰表示法为+ 2 3。逆波兰表达式的优点是运算符之间不必有优先级关系,也不必用括号改变运算次序,例如(2 + 3) * 4的逆波兰表示法为* + 2 3 4。本题求解逆波兰表达式的值,其中运算符包括+ - * /四个。

输入:
输入为一行,其中运算符和运算数之间都用空格分隔,运算数是浮点数
输出:
输出为一行,表达式的值。

样例输入:
* + 11.0 12.0 + 24.0 35.0
样例输出:
1357.000000
提示:(11.0+12.0)*(24.0+35.0)

逆波兰表达式的定义:
1)一个数是一个逆波兰表达式,值为该数
2)"运算符 逆波兰表达式 逆波兰表达式" 是逆波兰表达式 ,值为两个逆波兰表达式的值运算的结果

#include <iostream>
#include <cstdio>
#include <cstdlib>
using namespace std;
double exp() {
    //读入一个逆波兰表达式,并计算其值
    char s[20];
    cin >> s;
    switch(s[0]) {
        case +: return exp()+exp();
        case -: return exp()-exp();
        case *: return exp()*exp();
        case /: return exp()/exp();
        default: return atof(s);
        break;
    }
}    

int main()
{
    printf("%lf",exp());
    return 0;
}

用递归解决递归形式的问题

表达式计算

输入为四则运算表达式,仅由整数、+、-、*、/ 、(、) 组成,没有空格,要求求其值。假设运算符结果都是整数。"/"结果也是整数

 1 #include <iostream>
 2 #include <cstring>
 3 #include <cstdlib>
 4 using namespace std;
 5 int factor_value();
 6 int term_value();
 7 int expression_value();
 8 int main()
 9 {
10     cout << expression_value() << endl;
11     return 0;
12 }
13 
14 输入:(2+3)*(5+7)+9/3
15 输出: 63
16 
17 int expression_value() //求一个表达式的值
18 {
19     int result = term_value(); //求第一项的值
20     bool more = true;
21     while( more) {
22         char op = cin.peek(); //看一个字符,不取走
23         if( op == + || op == - ) {
24             cin.get(); //从输入中取走一个字符
25             int value = term_value();
26             if( op == + ) result += value;
27             else result -= value;
28         }
29         else more = false;
30     }
31     return result;
32 }
33 
34 int term_value() //求一个项的值
35 {
36     int result = factor_value(); //求第一个因子的值
37     while(true) {
38         char op = cin.peek();
39         if( op == * || op == /) {
40             cin.get();
41             int value = factor_value();
42             if( op == *)
43                 result *= value;
44             else result /= value;
45         }
46         else
47             break;
48     }
49     return result;
50 }
51  //求一个因子的值
52 int factor_value() //求一个因子的值
53 {
54     int result = 0;
55     char c = cin.peek();
56     if( c == () {
57         cin.get();
58         result = expression_value();
59         cin.get();
60     }
61     else {
62         while(isdigit(c)) {
63             result = 10 * result + c - 0;
64             cin.get();
65             c = cin.peek();
66         }
67     }
68     return result;
69 }

用递归将问题分解为规模更小的子问题进行求解

爬楼梯

树老师爬楼梯,他可以每次走1级或者2级,输入楼梯的级数,求不同的走法数
例如:楼梯一共有3级,他可以每次都走一级,或者第一次走一级,第二次走两级,也可以第一次走两级,第二次走一级,一共3种方法。

输入
输入包含若干行,每行包含一个正整数N,代表楼梯级数,1 <= N <= 30输出不同的走法数,每一行输入对应一行

输出
:不同的走法数,每一行输入对应一行输出
样例输入
5
8
10
样例输出
8
34
89

n级台阶的走法 =
先走一级后,n-1级台阶的走法 +
先走两级后,n-2级台阶的走法
f(n) = f(n-1)+f(n-2)

边界条件: n < 0 0 n = 0 1 n = 1 1
               n = 0 1 n = 1 1 n = 2 2

 1 #include <iostream>
 2 using namespace std;
 3 int N;
 4 int stairs(int n)
 5 {
 6     if( n < 0)
 7         return 0;
 8     if( n == 0 )
 9         return 1;
10     return stairs(n-1) + stairs(n-2);
11 }
12 
13 int main()
14 {
15     while(cin >> N) {
16         cout << stairs(N) << endl;
17     }
18     return 0;
19 }

放苹果

把M个同样的苹果放在N个同样的盘子里,允许有的盘子空着不放,问共有多少种不同的分法?5,1,1和1,5,1 是同一种分法。

输入
第一行是测试数据的数目t(0 <= t <= 20)。以下每行均包含二个整数M和N,以空格分开。1<=M,N<=10。
输出
对输入的每组数据M和N,用一行输出相应的K。
样例输入
1
7 3
样例输出
8

设i个苹果放在k个盘子里放法总数是 f(i,k),则:
k > i 时, f(i,k) = f(i,i)
k <= i 时,总放法 = 有盘子为空的放法+没盘子为空的放法
f(i,k) = f(i,k-1) + f(i-k,k)
边界条件?

#include <iostream>
using namespace std;
int f(int m,int n) {
    if( n > m )
        return f(m,m);
    if( m == 0)
        return 1;
    if( n <= 0 )
        return 0;
    return f(m,n-1) + f(m-n,n);
}

int main() {
    int t,m,n;
    cin >> t;
    while( t--) {
        cin >> m >> n;
        cout << f(m,n) << endl;
    }
    return 0;
}

算24

给出4个小于10个正整数,你可以使用加减乘除4种运算以及括号把这4个数连接起来得到一个表达式。现在的问题是,是否存在一种方式使得得到的表达式的结果等于24。 这里加减乘除以及括号的运算结果和运算的优先级跟我们平常的定义一致(这里的除法定义是实数除法)。 比如,对于5,5,5,1,我们知道5 * (5 – 1 / 5) = 24,因此可以得到24。又比如,对于1,1,4,2,我们怎么都不能得到24。

输入
输入数据包括多行,每行给出一组测试数据,包括4个小于10个正整数。最后一组测试数据中包括4个0,表示输入的结束,这组数据不用处理。
输出
对于每一组测试数据,输出一行,如果可以得到24,输出“YES”;否则,输出“NO”。
样例输入
5 5 5 1
1 1 4 2
0 0 0 0
样例输出
YES
NO

n个数算24,必有两个数要先算。这两个数算的结果,和剩余n-2个数,就构成了n-1个数求24的问题
枚举先算的两个数,以及这两个数的运算方式。
边界条件:一个数算24
注意:浮点数比较是否相等,不能用 ==

 1 #include <iostream>
 2 #include <cmath>
 3 using namespace std;
 4 double a[5];
 5 #define EPS 1e-6
 6 bool isZero(double x) {
 7     return fabs(x) <= EPS;
 8 }
 9 bool count24(double a[],int n)
10 {//用数组a里的 n个数,计算24
11     if( n == 1 ) {
12         if(isZero( a[0] - 24) )
13             return true;
14         else
15             return false;
16     }
17     double b[5];
18     for(int i = 0;i < n-1; ++i)
19         for(int j = i+1;j < n; ++j) { //枚举两个数的组合
20             int m = 0; //还剩下m个数, m = n - 2
21             for(int k = 0; k < n; ++k)
22                 if( k != i && k!= j)
23                    b[m++] = a[k];//把其余数放入b
24             b[m] = a[i]+a[j];
25             if(count24(b,m+1))
26                 return true;
27             b[m] = a[i]-a[j];
28             if(count24(b,m+1))
29                 return true;
30             b[m] = a[j]-a[i];
31             if(count24(b,m+1))
32                 return true;
33             b[m] = a[i]*a[j];
34             if(count24(b,m+1))
35                 return true;    
36             if( !isZero(a[j])) {
37                 b[m] = a[i]/a[j];
38             if(count24(b,m+1))
39             return true;
40         }
41         if( !isZero(a[i])) {
42             b[m] = a[j]/a[i];
43             if(count24(b,m+1))
44                 return true;
45         }
46     }
47     return false;
48 }    

 

以上是关于递归算法的主要内容,如果未能解决你的问题,请参考以下文章

JavaScript - 代码片段,Snippets,Gist

以下代码片段的算法复杂度

有人可以解释啥是 SVN 平分算法吗?理论上和通过代码片段[重复]

片段(Java) | 机试题+算法思路+考点+代码解析 2023

CSP核心代码片段记录

算法漫游指北(第十篇):泛型递归递归代码模板递归思维要点分治算法回溯算法