递归

279 阅读5分钟

递归算法是一种直接或者间接调用自身函数或者方法的算法。说简单了就是程序自身的调用。

递归算法就是将原问题不断分解为规模缩小的子问题,然后递归调用方法来表示 问题的解。(用同一个方法去解决规模不同的问题)

递归算法,顾名思义就是有两个大的阶段:递和归,即就是有去(递去)有回(归来)。

  • 递去:将递归问题分解为若干个规模较小,与原问题形式相同的子问题,这些子问题可以用相同的解题思路来解决
  • 归来:当你将问题不断缩小规模递去的时候,必须有一个明确的结束递去的临界点(递归出口),一旦达到这个临界点即就从该点原路返回到原点,最终问题得到解决。

递归思维是一种从下向上的思维方式,使用递归算法往往可以简化我们的代码, 而且还帮我们解决了很复杂的问题。递归算法的难点就在于它的逻辑性,一般设计 递归算法需要考虑以下几点:

  • 明确递归的终止条件
  • 提取重复的逻辑,缩小问题的规模不断递去
  • 给出递归终止时的处理办法

递归:你打开面前这扇门,看到屋里面还有一扇门。你走过去,发现手中的钥匙还可以打开它,你推开门,发现里面还有一扇门,你继续打开它。若干次之后,你打开面前的门后,发现只有一间屋子,没有门了。然后,你开始原路返回,每走回一间屋子,你数一次,走到入口的时候,你可以回答出你到底用这你把钥匙打开了几扇门。

循环:你打开面前这扇门,看到屋里面还有一扇门。你走过去,发现手中的钥匙还可以打开它,你推开门,发现里面还有一扇门(若前面两扇门都一样,那么这扇门和前两扇门也一样;如果第二扇门比第一扇门小,那么这扇门也比第二扇门小,你继续打开这扇门,一直这样继续下去直到打开所有的门。但是,入口处的人始终等不到你回去告诉他答案。

模型一: 在递去的过程中解决问题

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;
    } 
}