三步问题(力扣)n种解法 JAVA

目录

  • 题目:
  • 1、dfs:
  • [2、dfs + 备忘录(剪枝):](#2、dfs + 备忘录(剪枝):)
    • [(1)神器 HashMap 备忘录:](#(1)神器 HashMap 备忘录:)
    • [(2)数组 memo 备忘录:](#(2)数组 memo 备忘录:)
  • 3、动态规划:
  • [4、利用 static 的储存功能:](#4、利用 static 的储存功能:)
    • [(1)static 修饰 HashMap:](#(1)static 修饰 HashMap:)
    • [(2)static 修饰 memo 数组:](#(2)static 修饰 memo 数组:)
    • [(3)static 修饰 dp 数组 + 动态规划:](#(3)static 修饰 dp 数组 + 动态规划:)
  • [5、动态规划 + 优化常数空间:](#5、动态规划 + 优化常数空间:)
  • [6、数学 矩阵:](#6、数学 矩阵:)

题目:

三步问题。有个小孩正在上楼梯,楼梯有n阶台阶,小孩一次可以上1阶、2阶或3阶。实现一种方法,计算小孩有多少种上楼梯的方式。结果可能很大,你需要对结果模1000000007。

示例1:

输入:n = 3

输出:4

说明: 有四种走法

示例2:

输入:n = 5

输出:13

提示:

n范围在[1, 1000000]之间

解题思路:

记得多次取模防止爆掉int

1、dfs:

java 复制代码
class Solution {
	int INF = 1000000007;
    public int waysToStep(int n) {
          return dfs(n) % INF;
    }
    public int dfs(int n) {
    	 if(n < 0) return 0;
         if(n == 0) return 1;
         return ((dfs(n - 1) + dfs(n - 2)) % INF + dfs(n - 3)) % INF;
    }
}

显然超时,得剪枝


2、dfs + 备忘录(剪枝):

(1)神器 HashMap 备忘录:

java 复制代码
class Solution {
	int INF = 1000000007;
	Map<Integer, Integer> map = new HashMap<>();
    public int waysToStep(int n) {
          map.put(0, 1);
          return dfs(n) % INF;
    }
    public int dfs(int n) {
    	if(n < 0) return 0;
    	if(map.containsKey(n)) return map.get(n);
        int ans = ((dfs(n - 1)  + dfs(n - 2)) % INF + dfs(n - 3)) % INF;
        map.put(n, ans);
        return ans;
    }
}



当然神器也是有缺陷的即查询需要些许时间,可以试试用数组当备忘录

(2)数组 memo 备忘录:

java 复制代码
class Solution {
	int INF = 1000000007;
	public static int memo[];
    public int waysToStep(int n) {
    	  memo = new int[n + 1];
          memo[0] = 1;
          return dfs(n) % INF;
    }
    public int dfs(int n) {
    	if(n < 0) return 0;
    	if(memo[n] != 0) return memo[n];
    	int ans = ((dfs(n - 1) + dfs(n - 2)) % INF + dfs(n - 3)) % INF;
    	memo[n] = ans;
    	return memo[n];
    }
}


速度快了不少,还有突破空间


3、动态规划:

代码:

java 复制代码
class Solution {
    public int waysToStep(int n) {
        if(n <= 2) return n;
        if (n == 3) return 4;
        int[] d = new int[n + 1];
        d[1] = 1;
        d[2] = 2;
        d[3] = 4;
        for (int i = 4; i <= n; i++){
            d[i] = (d[i-1] + d[i-2]) % 1000000007 +d[i-3];
            d[i] %= 1000000007;
        }
        return d[n];
    }
}


不太理解为什么会比 dfs + 剪枝快这么多,这里给出的猜测是递归压栈耗费了大量时间

当然26ms还有突破空间


4、利用 static 的储存功能:

之前提到过Java的static修饰变量是有存储功能的不会因为创建新对象就恢复初始化

static简述 JAVA

(1)static 修饰 HashMap:

代码:

java 复制代码
class Solution {
	int INF = 1000000007;
	public static Map<Integer, Integer> map = new HashMap<>();
    public int waysToStep(int n) {
          map.put(0, 1);
          return dfs(n);
    }
    public int dfs(int n) {
    	if(n < 0) return 0;
    	if(map.containsKey(n)) return map.get(n);
        int ans = ((dfs(n - 1)  + dfs(n - 2)) % INF + dfs(n - 3)) % INF;
        map.put(n, ans);
        return ans;
    }
}


时间大概降了一半

(2)static 修饰 memo 数组:

为了更好储存memo数组要设置最大长度

代码:

java 复制代码
class Solution {
	int INF = 1000000007;
	public static int memo[] = new int[1000001];
    public int waysToStep(int n) {
          memo[0] = 1;
          return dfs(n) % INF;
    }
    public int dfs(int n) {
    	if(n < 0) return 0; 
        if(memo[n] != 0) return memo[n];
    	int ans = ((dfs(n - 1) + dfs(n - 2)) % INF + dfs(n - 3)) % INF;
    	memo[n] = ans;
    	return memo[n];
    }
}



速度也是之前的一半


(3)static 修饰 dp 数组 + 动态规划:

充分利用了动态规划的高效以及static的存储功能设立static变量j避免重复更新数据

代码:

java 复制代码
class Solution {
	public static int dp[] = new int[1000000];
	public static int j;
    public int waysToStep(int n) { 
        if(dp[n] != 0) return dp[n];
        if(n <= 2) return n;
        if (n == 3) return 4;
        if(j < 3){
         dp[0] = 1;
         dp[1] = 1;
         dp[2] = 2;
         dp[3] = 4;
         j  = 4;
        } 
        for (int i = j; i <= n; i++) dp[i] = ((dp[i-1] + dp[i-2]) % 1000000007 +dp[i-3]) % 1000000007;
        j = n;
        return dp[n];
    }
}


偶然能触及12ms的边边


5、动态规划 + 优化常数空间:

代码:

java 复制代码
class Solution {
    public int waysToStep(int n) {
        if (n == 1) return 1;
        if (n == 2) return 2;
        if (n == 3) return 4;
        long a = 1, b = 2, c = 4, d;
        while (--n >= 3) {
            d = a + b + c;
            a = b;
            b = c;
            c = d % 1000000007;
        }
        return (int) c;
    }
}

这是网上大佬写的也这么快是我没想到的

目前猜测唯一可能是static修饰变量每次创建新对象仍要创建一次数组,且创建数组需要耗费很多时间


6、数学 矩阵:

java 复制代码
class Solution {
    public static int  waysToStep(int n) {
        if (n == 1) {
            return 1;
        }
        if (n == 2) {
            return 2;
        }
        if (n == 3) {
            return 4;
        }
        long[][] m = {{1, 1, 1}, {1, 0, 0}, {0, 1, 0}};
        long[][] mn = pow(m, n - 3);
        long res = (mn[0][0] * 4 + mn[0][1] * 2 + mn[0][2] * 1) % 1000000007;
        return (int) res;
    }

    public static long[][] pow(long[][] a, int i) {
        long[][] val = {{1, 0, 0}, {0, 1, 0}, {0, 0, 1}};
        while (i != 0) {
            if ((i % 2) == 1)  {
                val = multiply(val, a);
            }
            a = multiply(a, a);
            i = i / 2;
        }
        return val;
    }


    public static long[][] multiply(long[][] a, long[][] b) {
        long[][] c = new long[3][3];
        for (int i = 0; i < 3; i++) {
            for (int j = 0; j < 3; j++) {
                long s = 0;
                for (int k = 0; k < 3; k++) {
                    s = (s + (a[i][k] % 1000000007 * b[k][j] % 1000000007)) % 1000000007;
                }
                c[i][j] = s % 1000000007;
            }
        }
        return c;
    }
}
 

网上大佬写的,真快,给我看傻了

相关推荐
冬夜戏雪1 分钟前
[学习日记]看书笔记
java·学习
初级程序员Kyle5 分钟前
开始改变第一天 JVM的原理到调优(2)
java·面试
程序员三明治30 分钟前
Spring AOP:注解配置与XML配置双实战
java·后端·spring·代理模式·aop·1024程序员节
第七序章40 分钟前
【C + +】unordered_set 和 unordered_map 的用法、区别、性能全解析
数据结构·c++·人工智能·算法·哈希算法·1024程序员节
DKPT1 小时前
JVM直接内存和堆内存比例如何设置?
java·jvm·笔记·学习·spring
草莓熊Lotso1 小时前
《算法闯关指南:优选算法--二分查找》--23.寻找旋转排序数组中的最小值,24.点名
开发语言·c++·算法·1024程序员节
文火冰糖的硅基工坊1 小时前
[嵌入式系统-150]:智能机器人(具身智能)内部的嵌入式系统以及各自的功能、硬件架构、操作系统、软件架构
android·linux·算法·ubuntu·机器人·硬件架构
郝学胜-神的一滴1 小时前
主成分分析(PCA)在计算机图形学中的深入解析与应用
开发语言·人工智能·算法·机器学习·1024程序员节
weixin_436525071 小时前
若依 - idea集成docker一键部署springboot项目(docker-compose)
java·1024程序员节