之前看过全排列,但是总感觉有点别扭,因为那时候还不能驾驭递归中的回溯,最近做poj1011的Sticks的时候发现又要用到回溯的思想,但是有点看不明白,于是沉下心来好好的看看。发现还是有收获的。于是决定好好写写自己的想法和大家好好分享一下。
首先我们来了解一下什么是递归:
要想理解递归就得先理解递归;哈哈,这就是递归了。在c语言里面我们可以直接认为就是一个函数调用自己本身的做法,比如最简单的斐波拉契数列就可以这样写;
#include<stdio.h>
int sum,s;
int f(int n){
if(n==1||n==2){
sum= 1;
}
else{
sum=f(n-1)+f(n-2);
}
return sum;
}
int main()
{
s=0;
printf("%d",f(5));
}
上面的例子其实就是一个简单的递归了,我考虑一下它是如何调用的;
我们注意到,我们第一次调用函数时其实就是语句:
printf("%d",f(5));
也就是第一次传递的形参是5,5传递进去后发现既不是2,也不是1于是将执行语句:
(1)
else
sum=f(n-1)+f(n-2);
这个时候实际上是发生了两次调用,分别是f(n-1),f(n-2),即f(4),f(3);
(2)
接下来、会先调用f(4),f(3)必须得等f(4)中的所有语句执行完成的时候才能接着执行:
else
sum=f(n-1)+f(n-2);
接下来首先调用f(4),于是得到f(3),f(2),在这一步,肯定得先继续调用f(3)
于是得到f(2)和f(1)执行: (3)
if(n==1||n==2){
sum= 1;
}
//然后执行
return sum;
}
得到f(2)=1,f(1)=1;
然后我们回到第(3)步继续执行f(2),同样会执行:
if(n==1||n==2){
sum= 1;
}
//然后执行
return sum;
}
然后我们会得到f(2)=1;
接着调用(2)部分的f(3),分别得到f(2)=1;f(2)=1;
至此为止所有的调用都结束了,但是我们还没有得到答案是吧?其实接下来的话就是回溯了,我们之前的这些所有的状态其实都是存储在一个类似于栈的结构中,如果我们用n的值表示当前的状态的话,那么这个栈中的数值应该是:
5 4 3 2 1 2 3 2 1
正如图所示,当所有递归调用的时候就是开始回溯,栈中的元素开始出栈:
出栈的顺序想必大家都应该知道是:1 2 3 2 1 2 3 4 5
前面每一步的值都确定了,故后面的值也能够得出来了,如3(但事实栈里面储存的是f(3)=f(2)+f(1)这个语句的)出栈的时候f(3)=f(2)+f(1)而f(2)=1,f(1)=1是已经得出了的,故我们可以得到f(3)=2。。。。。