深入解析递归算法

       递归是计算机科学的一个重要概念,递归的方法是程序设计中有效的方法,采用递归编写递归能使程序变得简洁和清晰.据我所知有很多大神都是“无递归不编程”可见递归的重要性和方便性。

       以下我简单介绍一个简单的程序来给大家讲一下递归的基本用法。

void recursive(int n, int base)
{

i++;
static string DIGIT_TABLE = "0123456789abcdef";
if (n >= base)
recursive(n / base, base);
cout << DIGIT_TABLE[ n % base ];
}
        

(recursive的意思就是递归的意思)这个程序很简单就是先调用recursive(n)这个函数接着调用recursive(n、base)这个函数一直到recursive(1)终止递归。

         注意,尽管recursive似乎在调用自身,但实际上它在调用自身的一个副本。该副本只是具有不同参数的另一个函数。任何时候只有一个副本是活动的;其余的都将被挂起。处理所有的簿记工作是计算机的事情,不是你的工作。如果对计算机来说簿记工作都太多了,你们就要考虑一下这个问题是否合适用递归来解决这个问题了。这个我们在下边还会提到,读者先不要急着专研这个问题。

       递归的4条基本规则

1.基本情形:至少有一种无须递归即可获得解决的情形

这个很容易理解,因为如果没有一种无须递归即可获得解决的情形,那么我们的递归岂不是成一个死循环了,在上例中recursive(1)就是一种无须递归即可获得解决的情形。

2.进展:任意递归调用必须向基本情形迈进。

这个其实跟上边的原则类似,我们只有一步步接近上边说的“一种无须递归即可获得解决的情形”才有可能结束我们的递归,例如上述程序中的recursive(n - 1) ,每次都减去1,使参数越来越接近1.

3.正确性假说:总是假设递归调用是有效的。

  乍看,这一假设似乎有点奇怪,但是回想一下,我们总是假设函数调用有效,因此假设递归调用有效实际上没有什么不同。和任意函数一样,递归历程需要从其他函数调用组合解决方案以获得一个解决方案。但是其他函数可以包含原始函数更简单的实例。

4.复利规则:绝对不要通过在单独递归调用中解决同一问题实例的方式来重复工作。

     你应该知道并不是在哪里使用递归都合适。我在这儿给大家 举下面一个例子。这个程序的作用是计算前N个整数的和。

int printInt(int n)//递归计算前n个整数的和
{
if (n == 1)
return 1;
else
{
return printInt(n - 1) + n;
}
}

这个问题完全没有必要用递归解决,一个for循环就可以轻易解决这个问题。因为我们知道递归其实就是反复调用函数本身,而调用函数是要消耗时间和空间的,每次调用都要进行内部堆栈的压入之后再一个个弹出,所以函数调用开销一般比较大,当然这是使用内联指令可节省的。所以我们这儿有个基本的原则,能用循环解决的问题就不要用递归。

  通过递归调用斐波那契数可说明了一个更加严重的问题。因为斐波那契数是递归定义的,编写递归历程来确定斐波那契数似乎很自然。但是在实际解决这个问题的时候,递归调用总次数比我们试图计算的斐波那契数列要更大,原因是递归调用重复了太多。所以我们要记住绝不要通过在单独递归调用中解决同一问题实例的方式来重复工作。

   到此我对递归的理解先告一段落,欢迎讨论


你可能感兴趣的:(算法)