一、前言
找工作面试时最喜欢问的是算法题,虽然我觉得有些取巧的算法题只是跟刷题量有关。但是为了找工作不得不加强算法,特别是基础算法,这是一个人基本功的体现。《算法导论》是不错的教材,不过大部头看起来确实需要发时间,到现在还只是大略看了一遍,很多推导过程没细心看下来,深以为憾,以后有时间还是要猛补。算法题中最能体现算法精髓的则非递归莫属了,我对递归一直总觉得是一知半解,为了加深自己的理解,决定把自己的一些想法记下来,方便更好的理清自己的思路,也恳请各路大牛指正。
二、递归算法初探
本段内容素材来自《linux C一站式编程》,作者是宋劲松老师,说实话这是目前看到的国内关于linux C编程的最好的一本技术书籍,强烈推荐!
关于递归的一个简单例子是求整数阶乘,n!=n*(n-1)!,0!=1 。则可以写出如下的递归程序:
int factorial(int n)
{
if (n == 0)
return 1;
else {
int recurse = factorial(n-1);
int result = n * recurse;
return result;
}
}
factorial
这个函数就是一个递归函数,它调用了它自己。自己直接或间接调用自己的函数称为递归函数。这里的factorial
是直接调用自己,有些时候函数A调用函数B,函数B又调用函数A,也就是函数A间接调用自己,这也是递归函数。如果觉得迷惑,可以把factorial(n-1)
这一步看成是在调用另一个函数--另一个有着相同函数名和相同代码的函数,调用它就是跳到它的代码里执行,然后再返回factorial(n-1)
这个调用的下一步继续执行。
为了证明递归算法的正确性,我们可以一步步跟进去看执行结果。记得刚学递归算法的时候,老是有丈二和尚摸不着头脑的感觉,那时候总是想着把递归一步步跟进去看执行结果。递归层次少还算好办,但是层次一多,头就大了,完全不知道自己跟到了递归的哪一层。比如求阶乘,如果只是factorial(3)跟进去问题不大,但是若是factorial(100)那就麻烦了。
事实上,我们并不是每个函数都需要跟进去看执行结果的,比如我们在自己的函数中调用printf函数时,并没有钻进去看它是怎么打印的,因为我们相信它能完成打印工作。我们在写factorial函数时有如下代码:
...
int recurse = factorial(n-1);
int result = n * recurse;
...