在牛客上做的一套标记为简单的算法题,题目是一只青蛙一次可以跳上一个台阶,也可以跳上两级台阶。求该青蛙跳上一个n级台阶总共有多少种跳法,要求时间复杂度O(n),空间复杂度O(1),时间复杂度说明不能超过两成循环,也不能占用太多空间。
输入1返回1,输入2返回2,输入4返回5
咱先说思路,如果输入1代表一个台阶,那就只有一种方法直接上台阶,如果输入2则代表两个台阶,要么是一个一个台阶上,要么是直接跳到第二个台阶所以返回2,你也可以算下如果第4个台阶就是5种上台阶的方式,如果第5个就是8种,如果第6个就是13种方式。。。以此类推
你会发现一个规律1个台阶=1种,2个台阶=2种,3个台阶=3种,4个台阶=5种,5个台阶=8种,6个台阶=13种...,也就是说除了前3个是输入和输出数对等,第4个就是5(第3个台阶的结果是3+第二个台阶的结果是2),第5个是8(第4个台阶的结果+第3个台阶的结果)以此类推
这样我们就推出一个公式:
f(1)=f1 结果:1
f(2)=f2 结果:2
f(3)=f(2)的结果+f(1)的结果 3=2+1;
(f4)=f(3)的结果+f(2)的结果 5=3+2;
f(5)=f(4)的结果+f(3)的结果 8=5+3;
.....
也就是
f(3)=f(n-1)+f(n-2)
f(4)=f(n-1)+)fn-2)
公式规律全部都是这样
咱们用递归方式解这道题,如果输入1或2则直接返回输入的数即可,否择开始递归计算,但是此种方式既浪费时间也浪费空间,它会进行指数级的暴涨进行重复计算,所以时间复杂度不过关O(2^n)
- public class dynmicgh {
-
- // 青蛙跳台阶案例
-
- /**
- * 青蛙跳台阶,跳到第二个台阶是二种方式,跳到第三种台阶是三种,
- * 跳到第10个台阶是多少种方式。
- * 这是个找规律的题,我发现跳2个阶梯是两种方式,跳3个是三种方式,跳4个是五种方式
- * 跳5个是8种,6个是13种。。。
- * f3=f3-1的结果+f3-2的结果 2+1=3
- * f4=f4-1的结果+f4-2的结果 3+2=5
- * f5=f5-1的结果+f5-2的结果 5+3=8
- * f6=f6-1的结果+f6-2的结果 8+5=13
- * ...以此类推,就知道第10个了
- */
-
-
- private static class FrogJumpStepsDemo {
-
- // 递归案例,费时,O(2^n) 存在大量重复计算
- private int jumpSteps(int n) {
- if (n == 1) {
- return 1;
- }
- if (n == 2) {
- return 2;
- }
-
- return jumpSteps(n - 1) + jumpSteps(n - 2);
- }
-
- }
-
- public static void main(String[] args) {
- dynmicgh.FrogJumpStepsDemo stepsDemo = new FrogJumpStepsDemo();
- System.out.println(stepsDemo.jumpSteps(3));
-
- }
- }
出现重复的计算,我们可以根据当前的计算进行缓存,如果递归发现重复的输入可以直接从缓存取出返回。时间复杂度为O(n)符合要求。空间复杂度O(n)
- Map
map = new HashMap(); -
- // 带备忘录的递归方式,就相当于将当前阶梯的计算结果存储一下,如果下次递归
- // 进来不用计算直接从存储的地方拿
- // 空间复杂度是O(n)
- private int jumpSteps1(int num) {
- if (num == 0) {
- return 1;
- }
-
- if (num <= 2) {
- return 2;
- }
-
- if (map.containsKey(num)) {
- return map.get(num);
- } else {
- map.put(num, jumpSteps1(num - 1) + (num - 2));
- return map.get(num);
- }
- }
其实我们这个的算法都是由各个的子的算法结果影响后一算法的结果,这种也叫做动态规划,上面都是自顶向下找,也就是输入4,则4-1+4-2,然后递归找到3,我们可以用自上向下找的,也需要继续优化空间复杂度用动态规划思想,优化递归用的栈空间
但是可不可以不用递归呢,是可以的,我们可以用for循依次计算出前面的值,再依次交换到变量里等到下次循环使用,直到达到输入的变量数
时间复杂度O(n),空间复杂度O(1)创建一次则不会再进行占用其他空间
主要就是变量的存储,每一次计算完的值则放入re,再把n1的值改为计算后的值,n2改为n1的值
假如输入的是3则进入循环re=3,n2=2,n1=3 返回结果3
假如输入的是4 ,需要计算上一步骤的3得到re=3,n2=2,n1=3,然后re=3+2,n2=3,n1=5,以此类推,就不用递归了
- // 此种方式循环将想要的结果相加即可
- // 动态规划 时间O(n) 空间O1
- public int jumpSteps2(int num) {
- int n1 = 2;
- int n2 = 1;
- int re = 0;
- if (num == 1 || num == 2) {
- return num;
- }
- for (int n = 3; n <= num; n++) {
- re = n1 + n2;
- n2 = n1;
- n1 = re;
- }
- return n1;
- }
输入5个台阶返回的结果,