leetcode—70、爬楼梯

70. 爬楼梯

假设你正在爬楼梯。需要 n 阶你才能到达楼顶。——小青蛙跳台阶

每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?

注意:给定 n 是一个正整数。

示例 1:

输入: 2
输出: 2
解释: 有两种方法可以爬到楼顶。
1.  1 阶 + 1 阶
2.  2 阶

示例 2:

输入: 3
输出: 3
解释: 有三种方法可以爬到楼顶。
1.  1 阶 + 1 阶 + 1 阶
2.  1 阶 + 2 阶
3.  2 阶 + 1 阶

1、暴力法搜索求解

/*
    1、暴力法:把所有可能爬的阶数进行组合,i为当前阶数,n为目标阶数
    climbStairs(i,n) = (i+1,n) + climbStairs(i+2,n);
    时间复杂度:O(2^n),树形递归的大小为 2^n,有大量的重复计算
    空间复杂度:O(n),递归树的深度可以达到n
*/
		
import java.util.*;

public class ClimbStairsFirst {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		
		Scanner sc = new Scanner(System.in);
		int n = sc.nextInt();
		
		int result = climbstairs(0, n);
		System.out.println(result);
	}

	private static int climbstairs(int i, int n) {
		// TODO Auto-generated method stub
		if(i > n) {
			return 0;
		}
		if(i == n) {
			return 1;
		}
		
		return climbstairs(i + 1, n) + climbstairs(i + 2, n);
	}

}

2、记忆化递归,对1中方法的改进

/*
	2、记忆化递归:解决冗余问题,将中间值进行存储,直接取值不进行重复计算
	加内存。递归状态减少,多了一个数组。
	时间复杂度:O(n),树形递归的大小为 n
	空间复杂度:O(n),递归树的深度可以达到n
*/
import java.util.*;

public class ClimbStairsSecond {

	public static void main(String[] args) {
		// TODO Auto-generated method stub

		Scanner sc = new Scanner(System.in);
		int n = sc.nextInt();
		
		int[] memory = new int[n+1];
		int result = climbstairs(0, n, memory);
		System.out.println(result);
	}
	
	private static int climbstairs(int i, int n, int[] memory) {
		// TODO Auto-generated method stub
		if(i > n) {
			return 0;
		}
		if(i == n) {
			return 1;
		}
		if(memory[i] > 0) {
			return memory[i];
		}
		
		memory[i] = climbstairs(i + 1, n, memory) + climbstairs(i + 2, n, memory);
		return memory[i];
	}

}

3、动态规划

/*
	3、动态规划
		这个问题可以被分解为一些包含最优子结构的子问题,即他的最优解可以由子问题的
	最优解来构建。
		第i阶可以由一下两种方法得到:
		  在第(i-1)阶后向上爬一阶;
		  在第(i-2)阶后向上爬两阶。
		所以到达第i阶的方法总数就是到第(i-1)阶和第(i-2)阶的方法数之和。
		令dp[i]表示能到达第i阶的方法总数:
		 	dp[i] = dp[i-1] + dp[i-2]
		时间复杂度:O(n),单循环用到了n
		空间复杂度:O(n),dp数组用了n的空间
*/

import java.util.Scanner;
import java.util.*;

public class ClimbStairsDp {

	public static void main(String[] args) {
		// TODO Auto-generated method stub

		Scanner sc = new Scanner(System.in);
		int n = sc.nextInt();
		
		int result = climbstairsDp(n);
		System.out.println(result);
	}	
	
	// 动态规划
	private static int climbstairsDp(int n) {
		// TODO Auto-generated method stub
		if(n== 0 || n == 1) {
			return n;
		}
		
		int[] dp = new int[n+1];
		// dp[0] = 1; // 不管怎样,数组下标指针肯定是从0开始的,所以要考虑0.有0个台阶,不需要爬,所以没有方法数(但从斐波那契角度,dp[0]=1)
		dp[1] = 1; // 一阶的方案
		dp[2] = 2; // 二阶的方案
		for(int i = 3; i <= n; i ++) {
			dp[i] = dp[i -1] + dp[i - 2];
		}
		
		return dp[n];
	}
}

4、斐波那契数:递推求解

/*
	4、斐波那契数:递推求解
	上述动态规划中的
		 dp[i] = dp[i-1] + dp[i-2]
	其实就是第i个斐波那契数,即
		 Fib(n) = Fib(n-1) + Fib(n-2);
		 Fib(0) = 1; —> 这里可以考虑,也可以不考虑 
		 Fib(1) = 1;
		 Fib(2) = 2.
	时间复杂度:O(n),单循环用到了n,需要计算第n个斐波那契数
	空间复杂度:O(1),使用常量级的空间迭代
*/
import java.util.Scanner;

public class ClimbStairsFibNum {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Scanner sc = new Scanner(System.in);
		int n = sc.nextInt();
		
		int result = climbstairsFib(n);
		System.out.println(result);
	}	
	
	// 斐波那契数
	private static int climbstairsFib(int n) {
		// TODO Auto-generated method stub
		if(n == 0 || n == 1) {
			return n;
		}
		
		int fib1 = 1;
		int fib2 = 2;
		for(int i = 3; i <= n; i ++) {
			int fib3 = fib1 + fib2;
			fib1 = fib2;
			fib2 = fib3;
		}
		
		return fib2;
	}
}

5、斐波那契递推数列

import java.util.Scanner;

public class ClimbStairsFibFormula {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		/*
		 5、斐波那契数列:直接公式求解
		 	时间复杂度:O(1ogn)
		 	空间复杂度:O(1),使用常量级的空间迭代
		 */
		Scanner sc = new Scanner(System.in);
		int n = sc.nextInt();
		
		int result = climbstairsFib(n);
		System.out.println(result);
	}	
	
	// 递推公式
	private static int climbstairsFib(int n) {
		// TODO Auto-generated method stub
		// 斐波那契数列: 初始化f(1) = 1; f(2) = 1;
        // 因为斐波拉切数列是1,1,2,3,5......这里相当于少了最前面那个1,所以要往后移1位
		double sqrt5 = Math.sqrt(5);
		double fibN = Math.pow((1+sqrt5) / 2, n+1) - Math.pow((1-sqrt5) / 2, n+1);
		
		return (int) (fibN / sqrt5);
	}

}

leetcode—70、爬楼梯_第1张图片


https://www.zhihu.com/question/25217301 

6、矩阵快速幂

<待补充>


7、总结

        答案要求的 f(n) 即是斐波那契数列的第 n 项(下标从 0开始)。

        斐波那契数列第 n 项的求解方法:

        (1)n 比较小的时候,可以直接使用过递归法求解,不做任何记忆化操作,时间复杂度是 O(2^n),存在很多冗余计算

        (2) 一般情况下,使用「记忆化搜索」或者「迭代」的方法,实现转移方程,时间复杂度和空间复杂度都可以做到 O(n)。

        (3)为了优化空间复杂度,可以不用保存 f(x - 2)之前的项,只用三个变量来维护 f(x)、f(x - 1) 和 f(x - 2),可以理解成将把「滚动数组思想」应用在了动态规划中,也可以理解成是一种递推,这样把空间复杂度优化到了 O(1)。

        (4)随着 n 的不断增大 O(n) 可能已经不能满足需求,可以用「矩阵快速幂」的方法把算法加速到 O(logn)

        (5)同时可以把 n 代入斐波那契数列的通项公式计算结果,但是如果用浮点数计算来实现,可能会产生精度误差。 

你可能感兴趣的:(LeetCode,动态规划,算法)