您的位置:首页 > 其它

阶乘递归(factorial recursive):

2012-03-14 10:47 211 查看
阶乘!4 = 4 * 3 * 2 * 1

可以用递归来表示为:

factorial(1) = 1

factorial(n) = n * factorial(n - 1)

其中n>1。

斐波纳契递归(fibonacci recursive)

1,1,2,3,5,8,13,21,34,55,89,144…………

斐波纳契数列的第一个和第二个数字都定义为1,后面的每个数字都为前两个数之和。

用递归表示为:

fibonacci(1) = fibonacci(2) = 1

fibonacci(n) = fibonacci(n-1) + fibonacci(n-2)

其中n>2。

实现递归必须满足两个条件:

1.基本条件(base case)的成立

实际上就是定义递归应该什么时候终止,比如在上面两个例子中,factorial(1) = 1和fibonacci(1) = fibonacci(2) = 1就是递归的基本条件,一旦当递归执行到满足基本条件时就是结束递归。

2.递归步骤

对于所有的n值,函数都是以其自身通过n值较小的函数来定义,也就是说,所有n值函数通过许多步骤来达到最终用n值较小的函数(基本条件)来定义。可以得知,递归函数就是反复执行递归步,直到n达到基本条件为止。

factorial的递归代码实现:

factorial:

static int factorial(int n) {

// 基本条件

1 if (n <= 1){

2 return 1;

}else{

// 递归步,执行n-1次

3 return n * factorial(n - 1);

}

}


分析:

语句3将被执行n-1次,一直到n<=1时结束递归。

假设n=4,那么我们可以得知:

第一步:调用factorial(4)

第二步:调用4 * factorial(3) n = 4

第三步:调用3* factorial(2) n = 3

第四步:调用2 * factorial(1) n = 2

第五步:返回1给factorial(2)

第六步:返回2给factorial(3)

第七步:返回6给factorial(4)

第八步:返回值24。

fibonacci:

static int fibonacci(int n){
// 基本条件
if (n <= 2){
return 1;
}else{
// 递归步,执行n-2次
return fibonacci(n-1) + fibonacci(n-2);
}
}


分析:可以用递归调用树来描述。每次都会两次调用到自己。

编写递归应注意事项:

----避免死循环,一般都是由于没有基本条件而造成,也可能是递归步的逻辑错误导致。

递归方法的运行实现原理:

我们发现,递归就是一个不停调用方法自身的一个过程,即方法的反复调用!

计算机是通过栈的机制来实现方法调用的。首先,我们来了解下计算机的方法调用机制:

1.程序执行前,计算机会在内存中创建一个调用栈 ,一般会比较大

2.当调用某个方法时,会有一个和该被调用方法相关的记录信息被推入到栈中

3.被推入到栈中的记录信息包括内容:传递到被调用方法中的参数值、该方法的局部变量、该方法的返回值。

4.当返回某个方法或者方法结束时,会从栈中取出对应的方法记录信息

栈的使用机制:后进先出(LIFO)。注意:最然递归方法简洁,但是效率不是完全就比迭代高,有时甚至低。因为我们考虑算法不仅要从时间、增长率来考虑,还要考虑空间(一般指内存)问题,递归的栈空间是我们必须考虑的,因为每次方法的调用都需额外的空间来存储相关信息。

递归和查找:

在前面,我们已使用迭代来实现了线性和二分查找,同样,这两种算法也能用递归来实现,但是线性查找用递归来实现并没任何优势,因此一般线性不采用递归。

线性查找的递归实现:

static boolean linearSearch(int[] array, int target, int pos){
if (pos >= array.length){
return false;
}
else if (array[pos] == target){
return true;
}else{
return linearSearch(array,target,pos + 1);
}
}


注意:pos = 0

二分查找的递归实现:

static boolean binarySearch(int[] array, int target, int front, int tail){
if (front > tail)	{
return false;
}else{
int middle = (front + tail) / 2;
if (array[middle] == target){
return true;
}
else if (array[middle] < target){
return binarySearch(array,target,middle + 1,tail);
}else{
return binarySearch(array,target,front,middle - 1);
}
}
}


二分查找的递归方法在平均和最差情况下的复杂度和用迭代实现二分的一样,都是O(logn)。其空间复杂度也为O(nlogn)。所以,用递归实现的二分查找也是可行的。

递归和迭代的选择:

在我们选择一个算法来实现一个方法时,我们应该对多个理由进行对比,高效+简洁+易维护就是最好的。一般,递归由于方法调用的时间和空间的开销,往往比相应的非递归方法效率低,但有时递归的精确和简洁有时称为用户首选。

尾递归:

递归调用之后算法不用再做任何工作,那这个算法就是尾递归。尾递归的主要特点就是编译器可将尾递归代码转换成机器语言形式的循环,从而不会产生与递归有关的开销。

例:阶乘的尾递归实现

static int factIter(int n, int result){
if (n <= 1){
return result;
}else{
return factIter(n-1, n*result);
}
}

递归和排序:

前面的几种排序的复杂度都是O(n^2),效率不高,这里我们要学习两种通过分治算法(递归)来实现的比较高效的排序方法,分别是快速排序和归并排序(扩展内容)。注意:效率越高其算法的编写复杂性自然也会提高
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: