简单的算法题-斐波拉契数列之跳台阶

一只青蛙一次可以跳上1级台阶,也可以跳上2级。求该青蛙跳上一个n级的台阶总共有多少种跳法(先后次序不同算不同的结果)。

1 简单粗暴的递归法

对于这种问题,只要找到递推关系即可,我们假设n级台阶一共有 f(n) 种跳法,那么这个第n个台阶可以由第 n-1 (倒数第二个)个台阶跳一次到达,也可以由第 n-2 (倒数第三个)个台阶,跳两次到达,故有:

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

public class Solution {
    public int jumpFloor(int target) {
        if(target == 1)
            return 1;
        if(target == 2)
            return 2;
        return jumpFloor(target - 1) + jumpFloor(target - 2);
    }
}

这是最简单粗暴的做法,但是同时也会存在栈崩溃的风险,因为 target 越大,递归的次数越多,最终导致无法得出结果。最主要的原因是 存在大量的重复计算 。比如你计算了
f(n) = f(n - 1) + f(n - 2)

  • f(n - 1) --> = f(n - 2) + f(n - 3)
  • f(n - 2) --> = f(n - 3) + f(n - 4)

这里,仅仅递归一次,就还要重复计算 f(n - 2) 和 f(n - 3) 各两次,随着递归的深入,重组计算量越来越大。

2 简单粗暴的递归法

把每次的计算结果存储起来,已经算过的表达式不再重新计算,这样可以避免上述问题。

package com.jianzhi.offer;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;


public class Jump {
	public static void main(String[] args) {
		Solution solution = new Solution();
		System.out.println(solution.jumpFloor(40));
	}
	
	static class Solution{
		List<Integer> list = new ArrayList(Collections.nCopies(1000, -1));
		public int jumpFloor(int target) {			
			if(target == 1)
				return 1;
			if(target == 2)
				return 2;
			if(list.get(target) > 0)
				return list.get(target);
			else {
				int result = jumpFloor(target - 1) + jumpFloor(target - 2);
				list.set(target, result);
				return result;
			}			
		}
	}
}


3 动态规划

核心思想还是利用状态转移方程 dp[n]=dp[n-1]+dp[n-2],这种方法是从最开始的台阶逐步向上走,最大的好处也是避免了重复计算。


public class Solution {
    public int JumpFloor(int target) {
        if(target == 0) return 0;
        if(target == 1) return 1;
        if(target == 2) return 2;
        int dp[] = new int[target + 1];
        dp[1] = 1;
        dp[2] = 2;
        for(int i = 3; i <= target; i++)
        {
            dp[i] = dp[i - 1] + dp[i - 2];
        }
        return dp[target];
    }
}

这题目还有其他解法,比如矩阵什么的,不是很常见,大家可自行查阅。

升级版跳台阶

现在青蛙跳到台阶n的方法升级了,可能青蛙变异了,它可以一次性跳一步,也可以一次性跳两步,甚至可以一次性就跳到n,那么跳到n又有多少种方法呢?

这个问题呢,其实也有递归和动态规划两种方法,只不过我们要考虑的是n,而不再是一次性跳1~2个台阶的问题了。

  • n = 2 时,有两种跳的方式,一阶跳和两阶跳,f(2) = f(1) + f(0) = 2
  • n = 3 时,有三种跳的方式,第一次跳出一阶后,后面还有f(3-1)中跳法; 第一次跳出二阶后,后面还有f(3-2)中跳法;第一次跳出三阶后,后面还有f(3-3)中跳法,f(3) = f(2) + f(1) + f(0) = 4
  • 当n = n 时,第一次跳出一阶后,后面还有f(n-1)中跳法; 第一次跳出二阶后,后面还有f(n-2)中跳法…第一次跳出n阶后,后面还有 f(n-n)中跳法,即:
    f(n) = f(n-1) + f(n-2) + f(n-3) + … + f(n-n) = f(0) + f(1) + f(2) + … + f(n-1)

这里其实用到了数学归纳法,又因为 f(n-1) = f(0) + f(2) + f(3) + … + f(n-2),故
两式相减得:
f(n) = 2 * f(n-1) ( n >= 2)

这里给出一个代码示意,其他方法可类比

public class Solution {
    public int JumpFloor(int target) {
        if(target <= 0)
	    	return 0;
	    if(target == 1)
	    	return 1;
	    int f = 1;
	    for(int i = 2; i <=  n;  i++){
	   		f = 2 * f;
	    }
	    return f;
    }
}
发布了23 篇原创文章 · 获赞 22 · 访问量 4万+

猜你喜欢

转载自blog.csdn.net/song_lee/article/details/100128189