递归法

递归法:

通过自身调用自身来达到问题解决的算法。一般,我们用归纳法是证明递归算法正确性和进行算法分析

\color{#6739b6} {{\textbf{为什么使用递归算法??}}}
原问题无法解决,用分治的的思想将其分解,若分解后的子问题仍无法解决就再次分解,直到分成的子问题规模足够小可以直接解决,然后将子问题合并得到原问题的解。

\color{#6739b6} {{\textbf{递归算法的思想??}}}
递归思想就是用与自身问题相似但规模较小的问题来描述自己。递归算法的执行过程划分为递推和回归两个阶段。在递推阶段,把规模为n的问题的求解推到比原问题的规模较小的问题求解,且必须要有终止递归的条件。在回归阶段,当获得最简单情况的解后,逐级返回,依次得到规模较大问题的解。就像打开一扇门里面还有一扇门,重复直到门里面没有门,然后再从一扇有一扇门中走出来。

\color{#6739b6} {{\textbf{可利用递归算法解决的问题所具有的特性????}}}
(1)求解规模为n的问题可以转化为一个或多个结构相同、规模较小的问题,然后从这些小问题的解能方便地构造出大问题的解。相邻两次重复之间有紧密的联系,前一次要为后一次做准备(通常前一次的输出就作为后一次的输入)。

(2)递归调用的次数必须是有限的,每次递归调用后必须越来越接近某种限制条件。

(3)必须有结束递归的条件(边界条件)来终止递归。当递归函数符合这个限制条件时,它便不在调用自身,即当规模n=1时,能直接得解。
举个栗子:(全排列、组合、深度优先搜索等。在数据结构中,树、二叉树和列表常采用递归方式来定义。)
一堆产品中有一个次品存在,若这个次品与合格品的区别只是重量的不同,要找出这个次品。用递归法解时:先将这一堆产品分成个数一样的两堆。进行称重后必然有一端重一端轻,此时还无法判断次品所在;然后继续对每部分的n/2个产品再进行分组和称重,如果平衡则这部分的n/2个产品都为正品,即次品在另外那部分中,否则次品就在这n/2个产品中。这样寻找次品的范围就减少了一半。依照这种方法不断缩小次品的范围,直到最后一对一找出次品。

再来一个栗子
求阶乘问题:

如图,递归过程在实现时,自身调用自身,层层向下进行,求解原问题的解时次序则正好相反

详细说说递推和回归:

递推时间复杂度计算):
递推方程-->递归方程

递推方程

递归方程

计算递推式的方法:
1、迭代法
将递推式先转换成一个和式,然后计算该和式,得到渐近复杂度。迭代方法需要较多的数学运算。原文连接:https://blog.csdn.net/u013340360/article/details/81030820

2、替换方法:猜递推式的解的范围(渐近复杂度的上下界),然后用数学归纳法证明是否存在满足条件的解。
3、公式法:
递归方程一般形式

计算过程

回归应用举例):

  • 汉诺塔问题


    把圆盘按大小顺序重新摆放在另一根柱子上:在三根柱子之间一次只能移动个圆盘。移动的时候始终只能小圆盘压着大圆盘。盘子只能在三个柱子上存放
#include<iostream>
using namespace std;
void move(int n,char x,char y)
{
        cout<<"把"<<n<<"号从"<<x<<"挪动到"<<y<<endl;
}
void Hannoi(int n,char A,char B,char C)    //将n个盘子从A柱借助B柱移动C柱
{
        if(n==1)                        //递归终止条件
        move(1,A,C);
    else
        {
           Hannoi(n-1,A,C,B);            //递归调用
            move(n,A,C);
            Hannoi(n-1,B,A,C);            //递归调用
        }
}
int main()
{
        int n;
        cout<<”请输入盘子的个数:”<<endl;
        cin>>n;
        Hannoi(n,'A','B','C');
        return 0;
}

经过分析得:
(1)将n个盘子上面的n-1个盘子借助C柱从A柱移到 B柱上,需要count(n-1)次;

(2)将A柱上第n个盘子移到C柱,花费1次;

(3)将B柱上的n-1个盘子借助A柱移到C柱,需要count(n-1)次;


移动次数的递推关系

移动次数也可用递归法计算:

#include <iostream>
using namespace std;
int main()
{
    int num,count=0;
    cout<<"请输入需要移动的盘子数目:"<<endl;
    cin>>num;
    int val(int);   //函数声明        
    if(num==0){
    cout<<"输入的数字必须大于0!"<<endl;
    return -1;
    }
    else
    cout<<"需要"<<val(num)<<"次"<<endl;
    return 0;
}
int val(int n){
    int c;
    if(n==1) c=1;
    else c=2*val(n-1)+1;
    return c;
}
  • 斐波那契数列问题


    求第n个数值
#include<iostream.h>
int fib(int n)
{
    int f=0;
    if(n==1) return 0;
    if(n==2) return 1;
    f=fib(n-1)+fib(n-2);
    return f;
}
int main()
{
    int n,i,m=0;
    cin>>n;
    m=fib(n);
    cout<<"第"<<n<<"项是"<<m<<endl;
    m=0;
    for(i=1;i<=n;i++)
        m=fib(i)+m;
        cout<<"前"<<n<<"项和是"<<m<<endl;
    return 0;
}
  • 八皇后问题


    在8×8格的国际象棋棋盘上放置八个皇后,使得任意两个皇后不能互相攻击,即任何行、列或对角线上不得有两个或两个以上的皇后。这样的一个格局称为问题的一个解。原文链接:[https://www.jianshu.com/p/65c8c60b83b8]
#include<iostream>
using namespace std;
const int Normalize=9;        //定义常量,用来统一数组下标
int Num=0;                //整型变量,记录方案数
int Queen[9];                //记录8个皇后所占用的列号
bool C[9];                    //C[1]~C[8]布尔型变量,判断当前列是否可行
bool L[17];                    //L[2]~L[16]布尔型变量,判断(i-j)对角线是否可行
bool R[17];                //R[2]~R[16]布尔型变量,判断(i+j)对角线是否可行
void check(int i)                //被调用函数
{
    int j;                        //循环变量,表示列号
    int k;                        //临时变量
    for(j=1;j<=8;j++)
    {
        if((C[j]==true) &&(R[i+j]==true)&&(L[i-j+Normalize]==true))
//表示第i行第j列可行
        {
            Queen[i]=j;            //占用位置(i,j)
            C[j]=false;                //修改可行标志,包括所在列和两个对角线
            L[i-j+Normalize]=false;
            R[i+j]=false;
            if(i<8)                //判断是否放完8个皇后
            {
                check(i+1);    //未放完8个皇后则继续放下一个
            }
            else                    //已经放完8个皇后
            {
                Num++;        //方案数加1
                cout<<"方案"<<Num<<":"<<"\t";//输出方案号
                for(k=1;k<=8;k++)
                    cout<<k<<"行"<<Queen[k]<<"列"<<"\t";//输出具体方案
                cout<<endl;    //换行
            }
            C[j]=true;                //修改可行标志,回溯
            L[i-j+Normalize]=true;
            R[i+j]=true;
        }                        //循环结束
    }
}                        //check函数结束
int main()                    //主函数
{
    int i;                        //循环变量
    Num=0;                    //方案数清零
    for(i=1;i<9;i++)                //置所有列可行
        C[i]=true;
    for(i=0;i<17;i++)            //置所有对角线可行
        L[i]=R[i]=true;
    check(1);                    //递归放置8个皇后,从第一行开始放
    return 0;
}
优缺点

优点
处理重复性计算时,递归往往使函数的定义和算法的描述简单明了、易于理解、容易编程和验证。
缺点
递归的运算方法,决定了它的效率较低,一是数据要不断进出栈,另一就是存在大量的重复计算,这样使得应用递归时,输入的n值稍大,程序的求解就变得比较困难,故在有些情况下,递归可以转化为效率较高的非递归。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 222,183评论 6 516
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 94,850评论 3 399
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 168,766评论 0 361
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,854评论 1 299
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,871评论 6 398
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 52,457评论 1 311
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,999评论 3 422
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,914评论 0 277
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 46,465评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,543评论 3 342
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,675评论 1 353
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 36,354评论 5 351
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 42,029评论 3 335
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,514评论 0 25
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,616评论 1 274
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 49,091评论 3 378
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,685评论 2 360

推荐阅读更多精彩内容