还在用递归,试试迭代吧

递归&迭代

递归(recursion)

递归常被用来描述以自相似方法重复事物的过程,在数学和计算机科学中,指的是在函数定义中使用函数自身的方法。(A调用A)

迭代(iteration)

重复反馈过程的活动,每一次迭代的结果会作为下一次迭代的初始值。(A重复调用B)

如题

有n步台阶,一次只能上1步或2步,共有多少种走法?

递归思路

  • n=1 ->一步 ->f(1)=1
  • n=2 ->(1)一步一步走 (2)直接走2步 ->f(2)=2
  • n=3 ->(1)先到达第一级台阶(f(1)),然后直接跨两步

      (2)先到达第二级台阶(f(2))     ->f(3) = f(1) + f(2)
  • n=4 -> (1)先到达f(2),然后直接跨两步
    (2)先到达第二级台阶f(3),然后直接跨一步 ->f(4) = f(2) + f(3)
  • ···
  • n=x (1)先到达f(x-2),然后直接跨两步
    (2)先到达第二级台阶f(x-1),然后直接跨一步 ->f(x) = f(x-2) + f(x-1)

    循环迭代思路

  • n=1 ->一步 ->f(1)=1
  • n=2 ->(1)一步一步走 (2)直接走2步 ->f(2)=2
  • n=3 ->(1)先到达第一级台阶(f(1)),然后直接跨两步

      (2)先到达第二级台阶(f(2))     ->f(3) = f(1) + f(2)
  • n=4 -> (1)先到达f(2),然后直接跨两步
    (2)先到达第二级台阶f(3),然后直接跨一步 ->f(4) = f(2) + f(3)
  • ···
  • n=x (1)先到达f(x-2),然后直接跨两步
    (2)先到达第二级台阶f(x-1),然后直接跨一步 ->f(x) = f(x-2) + f(x-1)

从上面能看出不管有多少台阶,都要走到最后的n-1或n-2台阶的这两种情况,所以我们可以用两个临时变量one=(f(n)-1)、two=(f(n)-2)保存这两个情况!

本题考点

  • 递归
  • 循环迭代

    题解

    递归方式

    public class Recursion {
    
      public static void main(String[] args) {
          long start = System.currentTimeMillis();
          System.out.println(f(42));
          long end = System.currentTimeMillis();
          System.out.printf("耗时毫秒为:%d", end - start);
      }
    
      /**
       * 有n步台阶,一次只能上1步或2步,共有多少种走法的递归代码实现
       * 性能损耗比较严重,特别是数据量大的时候,还会有可能造成栈溢出
       *
       * @param n 待计算的值
       * @return 计算结果
       */
      public static int f(int n) {
          if (n == 1 || n == 2) {
              return n;
          }
          return f(n - 2) + f(n - 1);
      }
    }

循环迭代

public class LoopIteration {

    public static void main(String[] args) {
        long start = System.currentTimeMillis();
        System.out.println(loopIteration(42));
        long end = System.currentTimeMillis();
        System.out.printf("耗时毫秒为:%d", end - start);
    }

    /**
     * 循环迭代解决 有n步台阶,一次只能上1步或2步,共有多少种走法问题
     *
     * @param n 待计算的值(台阶数)
     * @return 结果(多少种走法)
     */
    public static int loopIteration(int n) {
        if (n < 1) {
            throw new IllegalArgumentException(n + "不能小于1");
        }
        if (n == 1 || n == 2) {
            return n;
        }
        // 当n=3,one=f(3-1)=f(2)=2,two=f(3-2)=f(1)=1
        int one = 2; // 初始化走到最后需要走一步的走法
        int two = 1; // 初始化走到最后需要走两步的走法
        // 总步数
        int sum = 0;
        for (int i = 3; i <= n; i++) {
            sum = one + two;
            // 当i=4时,此时two=f(4-2)=f(2)=2,所以就是上一次的one
            two = one;
            // 当i=4时,one=f(4-1)=f(3)=3,所以就是上一次的sum
            one = sum;
        }
        return sum;
    }
  }

小结

  • 对比以上两种方式的执行时间,能看出递归多耗时间了吧,所以能用迭代的地方千万不要使用递归
  • 两种方式都是把大问题简化为小问题,从小问题一步一步推导出最终结果
  • 理论上所有的递归和迭代可以相互转换,但实际从算法结构来说,递归声明的结构并不总能转换为迭代结构(原因有待研究)
  • 方法调用自身称为递归,利用变量的原值推出新值称为迭代
  • 递归

    • 优点:大问题转化为小问题,可以减少代码量,同时代码精简,可读性好
    • 缺点:递归调用浪费了空间,而且递归太深容易造成堆栈溢出
  • 迭代

    • 优点:代码运行效率好,因为时间只因循环次数增加而增加,而且没有额外的空间开销
    • 缺点:代码不如递归简洁,可读性不太好(不易理解)

你可能感兴趣的:(java)