前言
递归是一种非常非常重要的编程思想,它可以把复杂的问题拆分成一个一个的子问题。虽然它并没有性能上的优势,甚至还有时候比循环的性能更糟糕,但是由于它在部分场合要比简单暴力的循环要容易理解得多,所以它仍然具有一定的学习价值。
汉诺塔问题(Hanoi)
想必很多朋友都玩过这种玩具,三个柱子,以及若干个从大到小的带孔圆盘,大的盘不可以放在小的盘上,盘必须套在柱子上,不可以放到一边,一次只能移动一个盘到另一个柱子上,如何让操作最省,将汉诺塔从最左边的柱子移向最右边的柱子呢?
我们来看一下笔者亲自画的图例(顺便一说,我研究了很久才搞明白如何搭建免费图床,如果后面有时间我也会写一个引用图片的教程)

这里有两个圆盘,也就是二阶的汉诺塔。
这里很简单,先把红色盘移到中间的柱子,再将黄色盘移到最右边,然后再将红色盘移动到最右边,三步,完成了。
那如果圆盘数量增加到3,4,5,6,……呢?
这里我们就要学会怎么模拟了,这里有三个圆盘,我们先将上面的两个小的看作一个整体,那么整体过程就可以看作是上面的二阶汉诺塔移动了。

所以,抽象地解释,汉诺塔的移动从本质上来说就分为三步:
- 把上方部分移到中间
- 把最大盘移到右边
- 把上方部分移到右边
请注意,如果把柱子从左往右标号为1,2,3的话,那么1、2号柱子的物理意义其实是相同的,怎么理解呢,也就是说,当最上方的n-1个圆盘存在于中介位置时,将其移动到目标柱子3号柱的过程是与它目前处在1还是2无关的,比如说存在于2号柱,也就是左下图的情况,你可以在脑海里将1和2两个柱子互换一下,就变得和上面n=2的物理过程一模一样了。
我们用代码来实现一下:
1 | void Hanoi(vector<int>& A, vector<int>& B, vector<int>& C) { |
爬楼梯
楼梯有 $ N $ 阶,上楼可以一步上一阶,也可以一步上二阶。
编一个程序,计算共有多少种不同的走法。
爬楼梯问题在实质上是一个斐波那契数列,斐波那契数列的通项公式是这样的:
\begin{equation} F [ n ] = F [ n - 1 ] + F [ n - 2 ] ( n > = 2 , F [ 0 ] = 1 , F [ 1 ] = 1 ) \end{equation}
知道了这个显然就很好编程了,但是我们还是来看一下原理的推导,我研究了许多篇博文,对原理的讲解都不算太侧重,今天我们详细地分解一下。
首先,思想上和上面的汉诺塔问题非常地相似,都是把复杂的问题化成一个个子问题的嵌套。
爬1阶楼梯,显然只有一种方法,即直接爬1阶。(不存在爬两阶超过它的情况)
爬2阶楼梯,则有两种方法,一次1阶,爬2次,和1次2阶,爬1次。
那么爬3阶楼梯呢,我们可以这么看,从最后上楼梯的方案来分类,如果最后一次爬了2阶,那么前面就一共爬了1阶,只有1种方法;如果最后一次爬了1阶,那么前面就一共爬了2阶,有2种方法。1+2,也就是3。
那么更多阶的情况,假设是n阶,都可以用n-1和n-2的情况去反推。
过河卒
实在是来不及写了,只画了个草图,先用这个讲课,回头再补全。

题目要求在这里:
棋盘上 A 点有一个过河卒,需要走到目标 B 点。卒行走的规则:可以向下、或者向右。同时在棋盘上 C 点有一个对方的马,该马所在的点和所有跳跃一步可达的点称为对方马的控制点。因此称之为“马拦过河卒”。
棋盘用坐标表示,A 点 (0,0)、B 点 (n,m),同样马的位置坐标是需要给出的。
现在要求你计算出卒从 A 点能够到达 B 点的路径的条数,假设马的位置是固定不动的,并不是卒走一步马走一步。
如果你想亲自去尝试一下这个题目,链接放在这里
这个题目的思路很简单,就是将地图抽象为一个矩阵,在C++里,你可以用二维数组去表示每一个交叉点。我们可以首先将马的控制范围标记为0,也就是说,这里是走不通的,然后从左上角开始往右下角开始标记,走到每个结点的可能方法数,是用走到该结点的左边和上面的结点的方法数之和,从左至右,从上至下依次完善这个矩阵数据,终点坐标的数值也就是所有可行的办法数了。
参考代码如下:
1 |
|