递归算法是一种直接或者间接调用自身函数或者方法的算法。说简单了就是程序自身的调用。
递归算法就是将原问题不断分解为规模缩小的子问题,然后递归调用方法来表示 问题的解。(用同一个方法去解决规模不同的问题)
递归算法,顾名思义就是有两个大的阶段:递和归,即就是有去(递去)有回(归来)。
- 递去:将递归问题分解为若干个规模较小,与原问题形式相同的子问题,这些子问题可以用相同的解题思路来解决
- 归来:当你将问题不断缩小规模递去的时候,必须有一个明确的结束递去的临界点(递归出口),一旦达到这个临界点即就从该点原路返回到原点,最终问题得到解决。
递归思维是一种从下向上的思维方式,使用递归算法往往可以简化我们的代码, 而且还帮我们解决了很复杂的问题。递归算法的难点就在于它的逻辑性,一般设计 递归算法需要考虑以下几点:
- 明确递归的终止条件
- 提取重复的逻辑,缩小问题的规模不断递去
- 给出递归终止时的处理办法
递归:你打开面前这扇门,看到屋里面还有一扇门。你走过去,发现手中的钥匙还可以打开它,你推开门,发现里面还有一扇门,你继续打开它。若干次之后,你打开面前的门后,发现只有一间屋子,没有门了。然后,你开始原路返回,每走回一间屋子,你数一次,走到入口的时候,你可以回答出你到底用这你把钥匙打开了几扇门。
循环:你打开面前这扇门,看到屋里面还有一扇门。你走过去,发现手中的钥匙还可以打开它,你推开门,发现里面还有一扇门(若前面两扇门都一样,那么这扇门和前两扇门也一样;如果第二扇门比第一扇门小,那么这扇门也比第二扇门小,你继续打开这扇门,一直这样继续下去直到打开所有的门。但是,入口处的人始终等不到你回去告诉他答案。
模型一: 在递去的过程中解决问题
function recursion(大规模){
if (end_condition){ // 明确的递归终止条件
end; // 简单情景
}else{ // 在将问题转换为子问题的每一步,解决该步中剩余部分的问题
solve; // 递去
recursion(小规模); // 递到最深处后,不断地归来
}
}
模型二: 在归来的过程中解决问题
function recursion(大规模){
if (end_condition){ // 明确的递归终止条件
end; // 简单情景
}else{ // 先将问题全部描述展开,再由尽头“返回”依次解决每步中剩余部分的问题
recursion(小规模); // 递去
solve; // 归来
}
}
斐波纳契数列
/**
- Title: 斐波纳契数列
- Description: 斐波纳契数列,又称黄金分割数列,指的是这样一个数列:1、1、2、3、5、8、13、21、……
- 在数学上,斐波纳契数列以如下被以递归的方法定义:F0=0,F1=1,Fn=F(n-1)+F(n-2)(n>=2,n∈N*)。
- 两种递归解法:经典解法和优化解法
- 两种非递归解法:递推法和数组法
* @author rico
*/
public class FibonacciSequence {
/**
* @description 经典递归法求解
*
* 斐波那契数列如下:
*
* 1,1,2,3,5,8,13,21,34,...
*
* *那么,计算fib(5)时,需要计算1次fib(4),2次fib(3),3次fib(2),调用了2次fib(1)*,即:
*
* fib(5) = fib(4) + fib(3)
*
* fib(4) = fib(3) + fib(2) ;fib(3) = fib(2) + fib(1)
*
* fib(3) = fib(2) + fib(1)
*
* 这里面包含了许多重复计算,而实际上我们只需计算fib(4)、fib(3)、fib(2)和fib(1)各一次即可,
* 后面的optimizeFibonacci函数进行了优化,使时间复杂度降到了O(n).
*
* @author rico
* @created 2017年5月10日 下午12:00:42
* @param n
* @return
*/
public static int fibonacci(int n) {
if (n == 1 || n == 2) { // 递归终止条件
return 1; // 简单情景
}
return fibonacci(n - 1) + fibonacci(n - 2); // 相同重复逻辑,缩小问题的规模
}
123456789101112131415161718192021222324252627282930313233
——————————–我是分割线————————————-
/**
* @description 对经典递归法的优化
*
* 斐波那契数列如下:
*
* 1,1,2,3,5,8,13,21,34,...
*
* 那么,我们可以这样看:fib(1,1,5) = fib(1,2,4) = fib(2,3,3) = 5
*
* 也就是说,以1,1开头的斐波那契数列的第五项正是以1,2开头的斐波那契数列的第四项,
* 而以1,2开头的斐波那契数列的第四项也正是以2,3开头的斐波那契数列的第三项,
* 更直接地,我们就可以一步到位:fib(2,3,3) = 2 + 3 = 5,计算结束。
*
* 注意,前两个参数是数列的开头两项,第三个参数是我们想求的以前两个参数开头的数列的第几项。
*
123456789101112131415
* 时间复杂度:O(n)
*
* @author rico
* @param first 数列的第一项
* @param second 数列的第二项
* @param n 目标项
* @return
*/
public static int optimizeFibonacci(int first, int second, int n) {
if (n > 0) {
if(n == 1){ // 递归终止条件
return first; // 简单情景
}else if(n == 2){ // 递归终止条件
return second; // 简单情景
}else if (n == 3) { // 递归终止条件
return first + second; // 简单情景
}
return optimizeFibonacci(second, first + second, n - 1); // 相同重复逻辑,缩小问题规模
}
return -1;
}
--------------------------------我是分割线-------------------------------------
/**
* @description 非递归解法:有去无回
* @author rico
* @created 2017年5月10日 下午12:03:04
* @param n
* @return
*/
public static int fibonacci_loop(int n) {
if (n == 1 || n == 2) {
return 1;
}
int result = -1;
int first = 1; // 自己维护的"栈",以便状态回溯
int second = 1; // 自己维护的"栈",以便状态回溯
for (int i = 3; i <= n; i++) { // 循环
result = first + second;
first = second;
second = result;
}
return result;
}
杨辉三角的取值
/**
* @description 递归获取杨辉三角指定行、列(从0开始)的值
* 注意:与是否创建杨辉三角无关
123
* @author rico
* @x 指定行
* @y 指定列
*/
/**
* Title: 杨辉三角形又称Pascal三角形,它的第i+1行是(a+b)i的展开式的系数。
* 它的一个重要性质是:三角形中的每个数字等于它两肩上的数字相加。
*
* 例如,下面给出了杨辉三角形的前4行:
* 1
* 1 1
* 1 2 1
* 1 3 3 1
* @description 递归获取杨辉三角指定行、列(从0开始)的值
* 注意:与是否创建杨辉三角无关
* @author rico
* @x 指定行
* @y 指定列
*/
public static int getValue(int x, int y) {
if(y <= x && y >= 0){
if(y == 0 || x == y){ // 递归终止条件
return 1;
}else{
// 递归调用,缩小问题的规模
return getValue(x-1, y-1) + getValue(x-1, y);
}
}
return -1;
}
}