数据结构和算法七

剑指 Offer 10- I. 斐波那契数列

题目:写一个函数,输入 n ,求斐波那契(Fibonacci)数列的第 n 项(即 F(N))。斐波那契数列的定义如下:
                   F(0) = 0, F(1) = 1
                   F(N) = F(N - 1) + F(N - 2), 其中 N > 1.
斐波那契数列由 0 和 1 开始,之后的斐波那契数就是由之前的两数相加而得出。
答案需要取模 1e9+7(1000000007),如计算初始结果为:1000000008,请返回 1。

示例 1:
       输入:n = 2
       输出:1
示例 2:
       输入:n = 5
       输出:5

/*解题思路:
	斐波那契数列的定义是f(n+1)=f(n)+f(n−1),生成第 nn 项的做法有以下几种:
递归法:
    原理:把f(n)问题的计算拆分成f(n−1)和f(n-2)两个子问题的计算,并递归,以f(0)和f(1)为终止条件。
    缺点:大量重复的递归计算,例如f(n)和f(n−1)两者向下递归需要各自计算f(n−2)的值。
记忆化递归法:
	原理:在递归法的基础上,新建一个长度为n的数组,用于在递归时存储f(0)至f(n)的数字值,重复遇到某数字则直接从数组取用,避免了重复的	递归计算。
	缺点:记忆化存储需要使用O(N)的额外空间。
动态规划:
    原理:以斐波那契数列性质f(n+1)=f(n)+f(n−1)为转移方程。
    从计算效率、空间复杂度上看,动态规划是本题的最佳解法。
动态规划解析:
    1、状态定义:设dp为一维数组,其中dp[i]的值代表斐波那契数列第i个数字。
    2、转移方程:dp[i+1]=dp[i]+dp[i−1],即对应数列定义f(n+1)=f(n)+f(n−1);
    3、初始状态:dp[0]= 0, dp[1] = 1,即初始化前两个数字;
    4、返回值:dp[n],即斐波那契数列的第n个数字。
空间复杂度优化:
	1、若新建长度为n的dp列表,则空间复杂度为O(N)。
	2、由于dp列表第i项只与第i−1和第i−2项有关,因此只需要初始化三个整形变量 sum, a, b ,利用辅助变量sum使a,b两数字交替前进即可     (具体实现见代码)。
	3、节省了dp列表空间,因此空间复杂度降至O(1) 。
*/

下图帮助理解递归法的 “重复计算” 概念。
数据结构和算法七_第1张图片

/*循环求余法:
	1、大数越界:随着n增大,f(n)会超过Int32甚至Int64的取值范围,导致最终的返回值错误。
	2、求余运算规则:设正整数x,y,p,求余符号为⊙,则有(x+y)⊙p=(x⊙p+y⊙p)⊙p。
解析:根据以上规则,可推出f(n)⊙p=[f(n−1)⊙p+f(n−2)⊙p]⊙p ,从而可以在循环过程中每次计算sum=(a+b)⊙1000000007,此操作与最终返回前取余等价。
复杂度分析:
时间复杂度O(N):计算f(n)需循环n次,每轮循环内计算操作使用O(1)。
空间复杂度O(1):几个标志变量使用常数大小的额外空间。
*/
class Method{
    public int fib(int n) {
        int a = 0, b = 1, sum;
        for(int i = 0; i < n; i++){
            sum = (a + b) % 1000000007;
            a = b;
            b = sum;
        }
        return a;
    }
}

剑指 Offer 10- II. 青蛙跳台阶问题

题目:一只青蛙一次可以跳上1级台阶,也可以跳上2级台阶。求该青蛙跳上一个 n 级的台阶总共有多少种跳法。
答案需要取模 1e9+7(1000000007),如计算初始结果为:1000000008,请返回 1。
示例 1:
        输入:n = 2
        输出:2
示例 2:
        输入:n = 7
        输出:21
示例 3:
        输入:n = 0
        输出:1
提示:
        0 <= n <= 100

/*
解题思路:
	1、此类求多少种可能性的题目一般都有递推性质,即f(n)和f(n−1)…f(1)之间是有联系的。
	2、设跳上n级台阶有f(n)种跳法。在所有跳法中,青蛙的最后一步只有两种情况:跳上1级或2级台阶。
		(1)当为1级台阶:剩n−1个台阶,此情况共有f(n−1)种跳法;
		(2)当为2级台阶:剩n−2个台阶,此情况共有f(n−2)种跳法。
	3、f(n)为以上两种情况之和,即f(n)=f(n−1)+f(n−2),以上递推性质为斐波那契数列。本题可转化为 求斐波那契数列第n项的值,与面试题10-I. 斐波那契数列等价,唯一的不同在于起始数字不同。
		(1)青蛙跳台阶问题:f(0)=1,f(1)=1,f(2)=2;
		(2)斐波那契数列问题:f(0)=0,f(1)=1,f(2)=1。
斐波那契数列的定义是f(n+1)=f(n)+f(n−1),生成第n项的做法有以下几种:
递归法:
    原理:把f(n)问题的计算拆分成f(n−1)和f(n−2)两个子问题的计算,并递归,以f(0)和f(1)为终止条件。
    缺点:大量重复的递归计算,例如f(n)和f(n−1)两者向下递归都需要计算f(n−2)的值。
记忆化递归法:
	原理:在递归法的基础上,新建一个长度为n的数组,用于在递归时存储f(0)至f(n)的数字值,重复遇到某数字时则直接从数组取用,避免了重复的递归计算。
	缺点:记忆化存储的数组需要使用O(N)的额外空间。
动态规划:
	原理:以斐波那契数列性质f(n+1)=f(n)+f(n−1)为转移方程。从计算效率、空间复杂度上看,动态规划是本题的最佳解法。
动态规划解析:
	1、状态定义:设dp为一维数组,其中dp[i]的值代表斐波那契数列第$i$个数字 。
	2、转移方程:dp[i+1]=dp[i]+dp[i−1],即对应数列定义f(n+1)=f(n)+f(n−1);
	3、初始状态:dp[0]=1,dp[1]=1,即初始化前两个数字;
	4、返回值:dp[n],即斐波那契数列的第n个数字.
空间复杂度优化:
	1、若新建长度为n的dp列表,则空间复杂度为O(N)。
	2、由于dp列表第i项只与第i−1和第i−2项有关,因此只需要初始化三个整形变量sum,a,b,利用辅助变量sum使a,b两数字交替前进即可(具体	    实现见代码)。
	3、因为节省了dp列表空间,因此空间复杂度降至O(1) 。
*/

实现思路原理图:
数据结构和算法七_第2张图片

/*循环求余法:
	1、大数越界:随着n增大,f(n)会超过Int32甚至Int64的取值范围,导致最终的返回值错误。
	2、求余运算规则:设正整数x,y,p,求余符号为⊙,则有(x+y)⊙p=(x⊙p+y⊙p)⊙p。
解析:根据以上规则,可推出f(n)⊙p=[f(n−1)⊙p+f(n−2)⊙p]⊙p,从而可以在循环过程中每次计算sum=a+b⊙1000000007,此操作与最终返回前取余等价。
复杂度分析:
    时间复杂度O(N):计算f(n)需循环n次,每轮循环内计算操作使用O(1) 。
    空间复杂度O(1):几个标志变量使用常数大小的额外空间。
*/
class Method{
    public int numWays(int n) {
        int a = 1, b = 1, sum;
        for(int i = 0; i < n; i++){
            sum = (a + b) % 1000000007;
            a = b;
            b = sum;
        }
        return a;
    }
}

总结:

首先,我要感谢各位小伙伴们!感谢你们能够在百忙之中抽时间来看我写的CSDN的博客,也不知道有没有对小伙伴们起到一些帮助呢?目前我所写的8篇文章已经被400多名小伙伴访问过了!在后续,我将持续更新!坚持每天发一篇!你们的访问是我前进的动力!愿小伙伴们都能有所收获!也愿我们都能够在互联网的行业中愈走愈远!一起加油!

你可能感兴趣的:(数据结构,java,算法)