台阶很高,青蛙跳不跳?
青蛙总是被被要求跳台阶,我想,他一定很累的!
一只青蛙一次可以跳上1级台阶,也可以跳上2级台阶。求该青蛙跳上一个 n 级的台阶总共有多少种跳法?
对于这样的问题,n可大可小,如果n很小,我们可以直观暴力拆解就可以得到答案,但是如果n很大,那么这个问题就升级了。
一般处理问题,我们最直接的思路,可能就是分治,将大问题拆解为小问题,分而解决。
在此,也不例外。
首先我们知道青蛙一次能跳一级或者两级。
假定最后一跳跳一级,则剩余n-1个台阶,则问题化为解决跳上n-1个台阶的问题。
假定最后一跳跳两级,则剩余n-2个台阶,则问题化为解决跳上n-2个台阶的问题。
所以归总起来,总的可能的跳法为(n-1)个台阶和(n-2)个台阶问题的总和。
我们假定解决方案为f(n),则f(n) = f(n-1) + f(n-2) ,这里我们假定n是大于2的。
当n = 1 时,青蛙跳一级即可,f(1) = 1。
当n = 2 时,青蛙可以连跳两个一级或者跳一个两级,f(2) = 2。
观察f(n) = f(n-1) + f(n-2) 公式,你们首先想到的是什么?对的,是递归,级联求解:
public static long jump(int n) {
if (n < 3) {
return n;
}
return jump(n - 1) + jump(n - 2);
}
我们以图像化展示一下这个过程:
图中以相同颜色标识了递归过程中会产生重复计算的节点。
重复是一种算力和资源不必要的浪费,我们可以对此进行优化:
对于上述的递归运算,我们可以看到,是由后至前计算的,也即从f(n)->f(1)。也就是我们需要知道向前的每一个位置的方案结果。我们换个方向,从前至后连续计算出每个位置的方案,则最后的位置即为我们所要的结果,同时也可以规避重复计算的问题:
代码实现:
public static long jumpx(int n) {
if (n < 3) {
return n;
}
//每个位置存储下标(i + 1)个台阶的可能结果f(i + 1),所以n个台阶即为计算f(n - 1)
Long[] arr = new Long[n];
arr[0] = 1L; //一个台阶
arr[1] = 2L; //两个台阶
//从 n = 3 开始循环计算
for (int i = 2; i < n; i++) {
arr[i] = arr[i - 1] + arr[i - 2];
}
return arr[n - 1];
}
我们通过增加一个长度为n的数组空间占用来换取算法耗时优化,相对于递归算法,耗时上有数量级差别。
耗时减少了,但是空间似乎浪费了,其实,也没必要存储每一个方案的结果,我们只需要知道【前一个】,【前两个】以及【当前】的几个变量。
改造如下:
public static long jumpy(int n) {
if (n < 3) {
return n;
}
//第三节台阶方案值f(3) = f(2) + f(1) = 1 + 2 = 3;
long preTwoCount = 1; //一个台阶
long preOneCount = 2; //两个台阶
long stepsCount = 0; //n个台阶
//从 n = 3 开始循环计算
for (int i = 2; i < n; i++) {
stepsCount = preOneCount + preTwoCount;
preOneCount = stepsCount;
preTwoCount = preOneCount;
}
return stepsCount;
}
空间复杂度降为O(1)。
- 续谈ActiveMQ之java如何操作ActiveMQ(springBoot项目)
- 深入理解JVM原理之编译openjdk7
- 初识ActiveMQ
- Kafka集群安装
- 知其所以然之永不遗忘的算法
- ZOOKEEPER集群搭建及测试
- 【Python环境】Scikit-Learn:开源的机器学习Python模块
- 【Python环境】可爱的 Python: 自然语言工具包入门
- 电脑静音工作,又听不到12306的来票音乐,纠结啊 !但春节前工作多任务重,不能安心工作,就动手做个“无声购票弹窗”工具吧!
- .net访问PostgreSQL数据库发生“找不到函数名”的问题追踪
- “领域驱动开发”实例之旅(1)--不一样的开发模式 一、分析业务需求。 二、设计领域对象模型 三、测试领域对象模型 四、设计业务处理类 五、设计Entity和Vi
- Java基础——左移和右移
- 【Python环境】利用 Python、SciKit 和文本分类来实现行为分析
- LJMM平台( Linux +Jexus+MySQL+mono) 上使用MySQL的简单总结
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 潘石屹用Python解决100个问题 | 阶乘之和
- 潘石屹用Python解决100个问题 | 斐波那契数列分数
- Leetcode No.7 整数反转
- 后端程序员必备的 Linux 基础知识+常见命令(近万字总结)
- XSS Game分析以及知识点总结
- Web 图形可视化 SQL 优化神奇,真香!
- C++ vector 容器浅析
- C++中的STL中map用法详解
- C++ pair(对组)的简单了解
- elasticSearch学习(六)
- centos7搭建LDAP服务器
- zookeeper is not a recognized option zookeeper参数不支持
- 快来看看你是不是“假的”DBA
- 基于docker搭建gitlab
- 理解Future及FutureTask的实现