本人的第一篇博客,纯原创,部分内容参考下面两位博主的文章,鸣谢!
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);
}
}
初步总结:对于编写递归程序,首先是得到其通用的处理步骤(规律),然后再分析其特殊部分,而特殊部分往往是递归函数返回(闭环)的关键。