递归问题的简单之处在于只要搞清楚宏观的几个步骤,照猫画虎,就可以借助电脑程序来帮助我们实现整个问题的解决。递归难在你总是想要搞清楚里面的每一个细节(会很绕的啊…)
汉诺塔问题我总结出的三点宏观步骤(假设有三个A B C柱子):
- 我们要将前n-1个盘子从A放到B中去,此时C是中转站
- 我们要将原本留在A的最大的那个盘子从A放到C,此时B是中转站
- 我们要将已经放到B中的n-1个盘子从B放到C中去,此时A是中转站
void move(int n,char from,char buffer,char to)
{
if(n==0)
return;
move(n-1,from,to,buffer);
printf("from %c to %c\n",from,to);
move(n-1,buffer,from,to);
}
以move(3,a,b,c)来举例,我们来看看递归的调用过程:
我们把当前的move函数当作一个宏函数,它内部有一个判断,一个子函数,一个输出,一个子函数。
当n=3的时候,首先进入到n-1的子函数(n=2),此时的n=2作为一个宏函数,再次进入到它的子函数(n=1),n=1作为一个宏函数,再次进入到它的子函数(n=0),此时,n=0想在进入到下一个子函数,结果因为判断条件,它不得不返回了。返回到哪里呢,回到了n=1的宏函数里的第一个n=0的子函数中了,之后,在n=1的宏函数中,继续向下走,遇到了输出,于是,输出当前的from和to,再继续向下走,又进入了n=0的子函数中了,前面我们已经说了,n=0回返回,到目前为止,n=1这个宏函数就经历完了函数里面的四个步骤了,于是它也返回了,返回到哪里呢?回到了以n=2的宏函数中的第一个子函数中,然后又继续往下走,遇到了输出,于是输出当前的from和to,又进入到了n=1的子函数中,有了刚才的经验,我们可以很清楚的知道,这个n=1的函数又要经过刚刚的那四个步骤(输出两次from和to了),于是呢这个n=2的函数就经历完了函数里的四个步骤了,于是它也返回了,返回到哪里呢,回到了n=3的宏函数内的第一个子函数中,于是继续往下走,遇到了输出,输出一个from和to;又遇到了第二个子函数了(n=2),这个子函数又要经历一遍刚才第一个子函数(n=2)经历的事情,经历完后,这个n=3的函数就可以返回了。
我们可以发现,它是有一种树的结构在里面的,大树里面有小树,小树里面有小小树,最小的那个叶子一定是n=0的时候了,比如说,我们从n=3,一直到n=0,好不容易走完了第一个子函数,每次返回上一层函数时,我们又发现第二个子函数又要重新来一遍。
想象一下,第一个子函数是一步到位,即逐层递减知道最小的n=0,但返回的时候却不是逐层返回,因为我们还有第二个子函数,随着返回的n值越大,第二个子函数要逐层递减的步骤就越多。(当n=1时,第二个子函数只需要递减一次(n=0),当n=2时,第二个子函数需要递减两次(n=1,n=0))每次的递减,都是作为一个宏函数来进行的。
说起来比较绕,但自己在图上画一画会清晰很多。