一、斐波拉切数列
题目描述
大家都知道斐波那契数列,现在要求输入一个整数n,请你输出斐波那契数列的第n项(从0开始,第0项为0)。n<=39
解题思路:
参考了这篇博客:https://www.cnblogs.com/edisonchou/p/4752052.html
对于斐波拉切数列,我们都很熟悉,在学递归方法的时候都讲过这个经典的运用递归的算法。
所以一种通常的解题方法就是用递归。
1.递归,但是递归的时间复杂度相当高,因为它在递归的过程中会计算一些重复的步骤。
越靠下面的元素要都要计算更多的遍数。
代码1:
public class Solution {
public int Fibonacci(int n) {
if(n>39){
System.out.println("n不能大于39!");
}
return Fib(n);
}
private int Fib(int n) {
// TODO Auto-generated method stub
if(n<=1){
return(n==1?1:0);
}
return Fib(n-1)+Fib(n-2);
}
}
2.从下往上顺序计算
这样可以避免计算上面这棵树中一些重复的节点。时间复杂度为o(n),耗时比递归方法减少了很多。
代码2:
public class Solution {
public int Fibonacci(int n) {
int[] init={0,1};
if(n<2){
return init[n];
}
int f_2=0;
int f_1=1;
int f_n=0;
for (int i = 2; i <= n; i++) {
f_n=f_2+f_1;
f_2=f_1;
f_1=f_n;
}
return f_n;
}
}
上图给出了两种方法的执行时间,可以看出递归的时间复杂度不是一般的大。
二、跳台阶
题目描述
一只青蛙一次可以跳上1级台阶,也可以跳上2级。求该青蛙跳上一个n级的台阶总共有多少种跳法(先后次序不同算不同的结果)。
如果只有n=1,则只有一种方法。
如果有n=2,则有两种方法,1+1或者2;
如果n>2,则第一次跳的时候有两种不同的选择,跳1台,则剩下的跳法与前面n-1台的一致。如果跳2台,则剩下的跳法与前面n-2台的一致。f(n)=f(n-1)+f(n-2).
所以此题的解法与上面解法一致。
代码:
public class Solution {
public int JumpFloor(int target) {
int[] init={1,2};
if(target<=2){
return init[target-1];
}
int t_2=1;
int t_1=2;
int t=0;
for (int i = 3; i <=target; i++) {
t=t_2+t_1;
t_2=t_1;
t_1=t;
}
return t;
}
}
用数组存储前面序列的值:
public class Solution {
public int JumpFloor(int target) {
if(target<=2){
return (target==2?2:1);
}
int[] ret=new int[target+1];
ret[1]=1;
ret[2]=2;
for (int i = 3; i < ret.length; i++) {
ret[i]=ret[i-2]+ret[i-1];
}
return ret[target];
}
}
三、变态跳台阶
参考:https://blog.youkuaiyun.com/qq_35082030/article/details/66975094
题目描述
一只青蛙一次可以跳上1级台阶,也可以跳上2级……它也可以跳上n级。求该青蛙跳上一个n级的台阶总共有多少种跳法。
n=1时,有一种方法,f(1)=1;
n=2时,有两种方法,1+1,2,所以f(2)=2;
n=3时,如果第一次跳1,则剩下的跳法有f(2)种;第一次跳2,则剩下的跳法有f(1)种;第一次跳3。f(3)=f(2)+f(1)+1;
n=4时,如果第一次跳1,则剩下的跳法有f(3)种;第一次跳2,则剩下的跳法有f(2)种;第一次跳3,则剩下的有f(1)种跳法;第一次跳4。 所以f(4)=f(3)+f(2)+f(1)+1
……
设1=f(0),
则为n时,f(n)=f(n-1)+f(n-2)+…+f(2)+f(1)+f(0);
而 f(n-1)= f(n-2)+…+f(2)+f(1)+f(0);上下相减,可得
f(n)=2f(n-1)
实现1:线性操作
public class Solution {
public int JumpFloorII(int target) {
if(target==1){
return 1;
}
int f=1;
for(int i=1;i<target;i++){
f*=2;
}
return f;
}
}
实现2:递归操作(不推荐)
public class Solution {
public int JumpFloorII(int target) {
if(target<=2){
return (target==2?2:1);
}
return 2*(JumpFloorII(target-1));
}
}
实现3:未化简之前(递归方法)
public class Solution {
public int JumpFloorII(int target) {
if(target<=2){
return (target==2?2:1);
}
int sum=0;
for (int i = 0; i <target; i++) {
sum+=JumpFloorII(i);
}
return sum;
}
}
对上面化简后的式子f(n)=2f(n-1)再次进行化简,
f(n)=2*2*f(n-2)=2*2*2*f(n-3)=2^(n-1)*f(1)
实现4:
public class Solution {
public int JumpFloorII(int target) {
return (int)Math.pow(2, target-1);
}
}
4.跳台阶,三种方式
题目:一个人爬楼梯,一次可以爬1级,2级,5级,如果楼梯一共有N级,总共有多少种走法?
实现1:递归
public int jump(int target){
if(target==0) return 1;
if(target==1) return 1;
if(target==2) return 2;
if(target<5){
return jump(target-1)+jump(target-2);
}else{
return jump(target-1)+jump(target-2)+jump(target-5);
}
}
实现2:DP
public int jump2(int target){
if(target<0) return 0;
if(target==1) return 1;
if(target==2) return 2;
int[] dp=new int[target+1];
dp[0]=1;
dp[1]=1;dp[2]=2;
if(target<=2){
return dp[target];
}
for (int i = 3; i <=target; i++) {
if(i<5){
dp[i]=dp[i-1]+dp[i-2];
}else{
dp[i]=dp[i-1]+dp[i-2]+dp[i-5];
}
}
return dp[target];
}
四、矩形覆盖
题目描述
我们可以用2*1的小矩形横着或者竖着去覆盖更大的矩形。请问用n个2*1的小矩形无重叠地覆盖一个2*n的大矩形,总共有多少种方法?
思路参考这篇博客:https://www.cnblogs.com/csbdong/p/5689674.html
如果竖着覆盖,则剩下的矩形覆盖方法有f(n-1)种。
如果横着覆盖,则与它对应位置的第二行也只能横着覆盖,则剩下的矩形的覆盖方法有f(n-2)种。
所以得出总的表达式:
n=1时,f(1)=1
n=2时,f(2)=2
n>2时,f(n)=f(n-1)+f(n-2)
得出的也是一个斐波拉切数列,实现方法与题目一一样。