关于递归算法学习的点滴感悟

Posted idream2018

tags:

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

本人的第一篇博客,纯原创,部分内容参考下面两位博主的文章,鸣谢!

https://www.cnblogs.com/GODYCA/archive/2013/01/15/2861545.html

https://www.cnblogs.com/LiCheng-/p/8206444.html (这篇文章写的不是很好,因为博文提到“每次问题规模缩小程度必须为1”,而事实上我在查阅资料时,这是需要根据实际情况确定的,比如”二分查找法”就是最好的例子,每次问题规模缩小程度就是一半)

 

  关于递归,今天逛园子的时候偶然发现的,一篇关于利用数学归纳法指导编写递归程序的博文,启发了我。但博主的文章写的不是那么好理解,于是我又琢磨了一下午,参考了别的资料,写了三个例子帮助自己理解。

 

关于数学归纳法,先看看是怎么回事:

 

一般地,证明一个与自然数n有关的命题P(n),有如下步骤:

(1)证明当n取第一个值n0时命题成立。n0对于一般数列取值为0或1,但也有特殊情况;

(2)假设当n=k(k≥n0,k为自然数)时命题成立,证明当n=k+1时命题也成立。

综合(1)(2),对一切自然数n(≥n0),命题P(n)都成立。

 

举个例子:

已知N!=N*(N-1)*(N-2)*(N-3)*…*2*1,求证R(N)与R(N-1)之间的关系!

第一步当N=1时可知 N!=1

第二步设当R(N)=N!,R(N-1)=(N-1)!

第三步,求R(N)与R(N-1)之间的关系:

因为R(N-1)=(N-1)!= (N-1)*…*2*1=> R(N)=N*R(N-1)

即:R(N)=N*R(N-1) 

写成一个函数求N!的值:

 factorial (int N)
    {

       if(N==1) return 1;        /*    特殊部分    */

       return N * factorial (N - 1)        /*    递归部分    */

    }

 

上面这个例子,利用数学归纳法得到R(N)=N*R(N-1) 的关系式恰好是递归写法的核心(通用)部分,而递归返回(特殊)部分为 if(N==1) return 1 语句。

那么编写递归程序就有指导思想:首先分析得到问题解决的通用处理步骤(规律),再处理递归返回(特殊)部分

 

下面看看三个例子帮助消化:

第一个例子(自己碰到的面试题,当时写不好,现在一下子就写出了):

现有如下关系:1、1、2、3、5、8、13…请用递归求出第150位数字的值?

分析:对于8=5+3,对于5=3+2,对于2=1+1,即第n位=第n-1位+第n-2位,其中n>2

         而对于第1位、第2位时,他们的值都是一样,即为1.

那我们可以很容易就可以写出来:

       int IndexNum (int n)

        {

            if(n==1||n==2)

            {

                return 1;

            }

            return IndexNum(n - 1) + IndexNum(n - 2);

        }

 

第二个例子,汉诺塔的实现,这个也很有意思(虽然书上很多):

 

  汉诺塔:三个立柱(命名为from、temp、to,from为圆盘初始所在立柱、to是目标立柱),N个直径不相等的圆盘,将圆盘从from上一个一个移动在to上,要求,每次只能移动一个圆盘,而且只能在三个立柱之间移动。目标柱to不能出现大盘压小盘的情况。

 

 首先用数学归纳法分析:


当只有一个圆盘的时候,我们可以确定:

直接将圆盘从from移动到to上:           move (n, from, to); 

 

现在假设有N个圆盘在from上,则需要进行如下操作:

首先需要将from上N-1个盘移动到temp上:   Hanoi (n-1, from, temp);

然后将from上的第N个盘移到to:           move (n, from, to);    

最后再将temp上N-1个盘移到to上:         Hanoi (n-1, temp, to); 

 

设Hanoi (int n, int from, int temp, int to)函数就是我们要求的汉诺塔实现函数,意义是将按直径递增摞在一起的n个圆盘从from按要求移动到to上,temp为辅助柱。

 

写出代码即:

void Hanoi (int n, int from, int temp, int to)

{

  if (n == 1)

  {

    move (n, from, to);

  }

  else

  {

    Hanoi (n-1, from, to, temp);

    move (n, from, to);

    Hanoi (n-1, temp, from, to);

  }

}

上面的代码,也同样包含通用规律,特殊部分即是递归的返回部分。

 

再看第三个例子,关于建立单向链表关系:

假设有节点,定义如下:

class node

{

     public int Num { get; set; }                 //序号

     public node next { get; set; }             //指向的下一个节点

 

     public node(int num)

     {

            Num = num;

     }

}

现在有数组nodes如下:

node[] nodes = new node[5];

nodes[0] = new node(2);

nodes[1] = new node(4);

nodes[2] = new node(1);

nodes[3] = new node(3);

nodes[4] = new node(9);

需将他们建立单向链关系:

 

首先分析:对于数组第n个节点,有nodes[n].next = nodes[n+1]的通用关系;

                对于最后一个节点,有nodes[4].next = null

那我们可以这么写:

void CreatLink(node[]data, int n)

{

     if (n >= data.Length-1)

     {

         data[n] = null;

         return;

     }else

     {

         data[n].next = data[n + 1];

         CreatLink(data, n + 1);

     }

}

初步总结:对于编写递归程序,首先是得到其通用的处理步骤(规律),然后再分析其特殊部分,而特殊部分往往是递归函数返回(闭环)的关键。

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

《算法图解》读书感悟

小鹤双拼输入法的点滴感悟

机器学习--DIY笔记与感悟--①K-临近算法

贪心算法学习,附由贪心算法引发的人生感悟。

学习递归时,影响到的一个算法问题

算法--排序--分治与快速排序