【算法】递归算法

1、基本介绍:【递归用的是数学归纳法】

  • 递归可以用来解决阶乘、斐波那契数列、汉诺塔、杨辉三角的存取、字符串反转、字符串全排列、二分查找、树的深度求解在内的八个经典问题。
  • 递归函数就是直接或间接调用自身的函数,也就是自身调用自己,并且有去(递去)有回(归来)。
  • “有去”是指:递归问题必须可以分解为若干个规模较小,与原问题形式相同的子问题,这些子问题可以用相同的解题思路来解决。
  • “有回”是指 : 这些问题的演化过程是一个从大到小,由近及远的过程,并且会有一个明确的终点(临界点),一旦到达了这个临界点,就不用再往更小、更远的地方走下去。最后,从这个临界点开始,原路返回到原点,原问题解决。

2、递归的三要素:

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

3、递归与循环的区别:

  • 单从算法设计上看,递归和循环并无优劣之别。然而,在实际开发中,因为函数调用的开销,递归常常会带来性能问题,特别是在求解规模不确定的情况下;而循环因为没有函数调用开销,所以效率会比递归高。
  • 递归求解方式和循环求解方式往往可以互换。
  • 递归转换为循环的步骤:
    ○ 自己建立“堆栈(一些局部变量)”来保存这些内容以便代替系统栈,比如树的三种非递归遍历方式;
    ○ 把对递归的调用转变为对循环处理。

4、经典的递归算法:

4.1、阶乘问题:
    public class Factorial {

        public static void main(String[] args) {
            System.out.println(factorial_recursive(9));
            System.out.println(factorial_loop(9));
        }

        /**
         * 递归解决
         * @param n 阶乘数目
         * @return
         */
        public static long factorial_recursive(int n) {
            if (n == 1) {  // 递归终止条件
                return 1;    // 简单情景
            }
            return n * factorial_recursive(n - 1);  // 相同重复逻辑,缩小问题的规模
        }

    //--------------------------------我是分割线-------------------------------------
        /**
         * 循环解决
         * @param n 阶乘数目
         * @return
         */
        public static long factorial_loop(int n) {
           long result = n;  //建立局部变量
           while (n > 1) {
                n--;
                result = result * n;
            }
            return result;
        }
    }
4.2、斐波纳契数列 :【黄金分割数列】
  • 指的是这样一个数列:1、1、2、3、5、8、13、21、34、……
  • 在数学上,斐波纳契数列以如下被以递推的方法定义:F(1)=1,F(2)=1, F(n)=F(n-1)+F(n-2)(n>=2,n∈N*),这个数列从第3项开始,每一项都等于前两项之和。
  • 这里面包含了许多重复计算,所以需要重新找规律进行优化处理,防止重复计算:以1,1开头的斐波那契数列的第六项正是以1,2开头的斐波那契数列的第五项,而以1,2开头的斐波那契数列的第五项也正是以2,3开头的斐波那契数列的第四项,……..,以此内推。时间复杂度是O(n)。
    public class FibonacciSequence {

        public static void main(String[] args) {
            System.out.println(fibonacci_recursive(10));
            System.out.println(optimizeFibonacci_recursive(1, 1, 10));
            System.out.println(fibonacci_array(10));
        }

        /**
         * 普通递归解法,未考虑到重复计算的优化
         *
         * @param n
         * @return
         */
        public static int fibonacci_recursive(int n) {
            if (n > 0) {
                if (n == 1 || n == 2) {
                    return 1;
                }
                return fibonacci_recursive(n - 1) + fibonacci_recursive(n - 2);
            }
            return -1;
        }

    //--------------------------------我是分割线1-------------------------------------
        /**
         * 对普通递归法的优化,时间复杂度是O(n)
         *
         * @param first  数列的第一项
         * @param second 数列的第二项
         * @param n      目标项:我们想求的以first, second开头的数列的第几项
         * @return
         */
        public static int optimizeFibonacci_recursive(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_recursive(second, first + second, n - 1);  // 相同重复逻辑,缩小问题规模
            }
            return -1;
        }

    //--------------------------------我是分割线2-------------------------------------
        /**
         * 循环解法
         *
         * @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;
        }

    //--------------------------------我是分割线3-------------------------------------
        /**
         * 使用数组存储斐波那契数列
         *
         * @param n
         * @return
         */
        public static int fibonacci_array(int n) {
            if (n > 0) {
                int[] arr = new int[n];   // 使用临时数组存储斐波纳契数列
                arr[0] = arr[1] = 1;
                for (int i = 2; i < n; i++) {   // 为临时数组赋值
                    arr[i] = arr[i - 1] + arr[i - 2];
                }
                return arr[n - 1];
            }
            return -1;
        }
    }
4.3、杨辉三角的取值:

        1
       1 1
      1 2 1
     1 3 3 1
    1 4 6 4 1
   1 5 10 10 5 1
  1 6 15 20 15 6 1
 1 7 21 35 35 21 7 1
1 8 28 56 70 56 28 8 1

  • 杨辉三角形第n层(顶层称第0层,第1行,第n层即第n+1行,此处n为包含0在内的自然数)正好对应于二项式〖(a+b)〗^2 的展开系数。
  • 例如第二层1 2 1是幂指数为2的二项式〖(a+b)〗^2 展开形式a^(2 )+2ab+b^2 的系数。
    public class PascalTriangle {

        public static void main(String[] args) {
            System.out.println(getValue(6,3));
        }

        /**
         * 递归解法
         *
         * @param x 指定行
         * @param y 指定列
         * @return
         */
        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;
        }
    }
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值