首页 > 技术文章 > 递归用法总结

zuixime0515 2019-03-11 21:17 原文

递归定义

递归调用:在调用一个函数的过程中,这个函数本身又直接或间接地调用了函数本身。
通俗地说,举个例子。有5个学生在一起,问第5个学生有多少钱,他说比第4个学生多2块,第四个又说比第三个多2块,第三个比第二个多2块,第二个又比第一个多2块。第一个人说自己只有10块。在这个例子中,我们求第5个人有多少钱的时候,可以通过第四个人,第四个通过第三个,一次类推。这就是递归的思想。
上述求学生钱的例子表达式可以用以下表达式:

    money(5) = money(4) + 2;
    money(4) = money(3) + 2;
    money(3) -= money(2) + 2;
    money(2) = money(1) +2;
    money(1) = 10;

数学公式如下

money(n) = 10;    (n=1)
money(n) = money(n-1) + 2; (n>1)

总的来说,递归要有以下两个条件:

  1. 基线条件:也就是停止条件
    2.递归条件:递归关系的表述

递归的几个经典题目

斐波那契数列

斐波那契数列是:0,1,1,2,3,5,8,13,21,34,55,89,144……依次类推下去,你会发现,它后一个数等于前面两个数的和。在这个数列中的数字,就被称为斐波那契数。
仔细观察我们可以知道,从第三项开始,当前项都等于前两项之和。这就是递归的条件。

long fac(long n){
   if(n == 1 || n == 0){
               return n;
    }
   if(n < 0)
        return -1;            //-1代表错误
   
   return fac(n - 1) + fac(n - 2);
}

阶乘问题

递归表达式:n! = n * (n-1)!

long f(int n){
    if( n == 1 || n == 0)
        return 1;
    if(n < 0)
        return -1;

    return f(n-1) * n;
}

汉诺塔问题

法国数学家爱德华·卢卡斯曾编写过一个印度的古老传说:在世界中心贝拿勒斯(在印度北部)的圣庙里,一块黄铜板上插着三根宝石针。印度教的主神梵天在创造世界的时候,在其中一根针上从下到上地穿好了由大到小的64片金片,这就是所谓的汉诺塔。不论白天黑夜,总有一个僧侣在按照下面的法则移动这些金片:一次只移动一片,不管在哪根针上,小片必须在大片上面。僧侣们预言,当所有的金片都从梵天穿好的那根针上移到另外一根针上时,世界就将在一声霹雳中消灭,而梵塔、庙宇和众生也都将同归于尽。

简单概述如下:
有三根杆子X,Y,Z。X杆上有N个(N>1)穿孔圆盘,盘的尺寸由下到上依次变小。要求按下列规则将所有圆盘移至Y杆:

  1. 每次只能移动一个圆盘;
  2. 大盘不能叠在小盘上面;

实现如下

void hanoi(int n,char from,char tmp,char to){
if (n>0) {
    hanoi(n - 1, from, to, tmp);
    cout<<"take " + n + " from " + from + " to " + to;
    hanoi(n - 1, tmp, from, to);
}
}

倒叙输出一个正整数

说明:给出正整数 n=12345,希望以各位数的逆序形式输出,即输出54321

递归思想:先输出个位数字,然后在输出个位数字前面的数字,知道没有数字

代码实现如下:

void printNum(int n){
     cout << n % 10 << " ";
     if( n > 10){
            printNum(n / 10);
     }
}

排序问题

用递归实现:输入一个字符串,打印出该字符串中字符的所有排列。例如输入字符串abc,则输出由字符a、b、c所能排列出来的所有字符串abc、acb、bac、bca、cab和cba。

递归思想
假如针对abc的排列,可以分成

  1. 以a开头,加上bc的排列

  2. 以b开头,加上ac的排列

  3. 以c开头,加上ab的排列

    void strfac(string s, int k){

     //递归出口条件:当交换到最后时就输出整个字符串
     if(k == s.length())
     {
         for(int i = 0; i < k; i++){
     	    cout << s[i] << " ";
         }
         cout <<endl;
     }
    
    
     //开始递归的算法
     for(int i = k; i < s.length(); i++){
    
         //将k个字符交换
         char t = s[k];
         s[k] = s[i];
         s[i] = t;
    
         //而后递归调用k+1个字符
         strfac(s,k+1);
    
    
         //回溯后变回原来的字符
         {
     	    char t = s[k];
     	    s[k] = s[i];
     	    s[i]= t;
         }
     }
    

    }

递归求从 m 个球中摸n个球的排列数

int getSum(int m,int n){

//判断n 是否大于m
if(m < n){
	cout << "摸得球数不能大于球总数" << endl;
	return 0;
}

if( m == n)
	return 1;
if(n == 0)
	return 0;

return getSum(m-1,n-1) +getSum(m-1,n);

}

求两个子串的最大公共序列长度

int getsublen(string s1,string s2){

    if(s1.length() == 0 || s2.length() == 0)
	    return 0;

    if(s1.at(0) == s2.at(0))
	    return getsublen(s1.substr(1),s2.substr(1)) + 1;
    //不相等则子串加一
    else
	    return max(getsublen(s1.substr(1),s2),getsublen(s1,s2.substr(1)));
}

int max(int a,int b){

    if(a > b)
	return a;
    else
	    return b;
}

推荐阅读