1.用两个栈实现队列
题目描述
用两个栈实现一个队列。队列的声明如下,请实现它的两个函数 appendTail 和 deleteHead ,分别完成在队列尾部插入整数和在队列头部删除整数的功能。(若队列中没有元素,deleteHead 操作返回 -1 )
解题思路
在新建类的时候,给对象初始化两个数组:stack1、stack2,作为栈。(我们知道,栈是先进后出,只有栈顶可以进出元素,这里我们可以假定数组的开头为栈顶,所以我们只能对这两个数组的开头进行元素增、减操作)
要实现的功能:先进先出
在执行函数appendTail时向stack1的开头插入要添加的值;在执行deleteHead函数时,先判断stack2是否为空,若为空,则从stack1数组中,从开头取值放入stack2的开头,再从stack1开头取值放入stack2的开头...直至将stack1中元素取完。
代码实现
class Quene {
constructor () {
this.stack1 = []
this.stack2 = []
}
appendTail (value) {
// 新插入的元素都放在stack1中
this.stack1.unshift(value)
}
deleteHead () {
// 删除的元素从stack2中删除,如果stack2空,就从stack1的末尾取
if ( !this.stack2.length ) {
for (let i=0; i<this.stack1.length; i++) {
this.stack2.unshift(this.stack1.pop())
}
}
return this.stack2.length ? this.stack2.shift() : -1
}
}
let quene = new Quene()
quene.appendTail(1)
quene.appendTail(2)
console.log(quene.deleteHead())
quene.appendTail(3)
quene.appendTail(4)
console.log(quene.deleteHead());
console.log(quene.deleteHead());
图解
let quene = new Quene()
quene.appendTail(1)
let quene = new Quene()
quene.appendTail(1)
quene.appendTail(2)
let quene = new Quene()
quene.appendTail(1)
quene.appendTail(2)
console.log(quene.deleteHead());
执行deleteHead删除一个元素操作时,会先判断stack2是否为空,此时为空;则从stack1数组中,从开头取值放入stack2的开头,再从stack1开头取值放入stack2的开头...直至将stack1中元素取完。
接着再执行stack2开头处的删除操作:
let quene = new Quene()
quene.appendTail(1)
quene.appendTail(2)
console.log(quene.deleteHead());
quene.appendTail(3)
quene.appendTail(4)
let quene = new Quene()
quene.appendTail(1)
quene.appendTail(2)
console.log(quene.deleteHead());
quene.appendTail(3)
quene.appendTail(4)
console.log(quene.deleteHead());
执行deleteHead删除一个元素操作时,会先判断stack2是否为空,此时不为空;则直接执行stack2开头处的删除操作:
let quene = new Quene()
quene.appendTail(1)
quene.appendTail(2)
console.log(quene.deleteHead());
quene.appendTail(3)
quene.appendTail(4)
console.log(quene.deleteHead());
console.log(quene.deleteHead());
console.log(quene.deleteHead());
执行deleteHead删除一个元素操作时,会先判断stack2是否为空,此时为空;则从stack1数组中,从开头取值放入stack2的开头,再从stack1开头取值放入stack2的开头...直至将stack1中元素取完。
接着再执行stack2开头处的删除操作:
2. 查询斐波那契数列的项
题目描述
斐波那契数列:第0项是0,第一项是1,第二项是1,后面第n项是第n-1项与第n-2项的和。
写一个函数,输入 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。
解题思路
尾调用优化:函数return了另一个函数。
思路是从前两项0、1开始,执行n次两项相加:
比如当n=3,求Fn(3):
第一次: 0 + 1 = 1
第二次: 1 + 1 = 2
第三次: 2 + 1 = 3
代码实现
var fib = function(n) {
return fibImpl(0, 1, n);
};
function fibImpl(a, b, n) {//a,b 分别对应 F(0), F(1)
if (n == 0) {
return a;
}
//结果要取模
return fibImpl(b, (a + b)%1000000007 , n - 1);
}
3.青蛙跳台阶的可能性有多少种
此类求 多少种可能性 的题目一般都有 递推性质 ,即 f(n)f(n) 和 f(n-1)f(n−1)…f(1)f(1) 之间是有联系的。
设跳上 n 级台阶有 f(n) 种跳法。在所有跳法中,青蛙的最后一步只有两种情况: 跳上 1 级或 2 级台阶。 当为 1 级台阶: 剩 n−1 个台阶,此情况共有 f(n−1) 种跳法; 当为 2 级台阶: 剩 n−2 个台阶,此情况共有 f(n−2) 种跳法。 f(n)为以上两种情况之和,即 f(n)=f(n-1)+f(n-2) ,以上递推性质为斐波那契数列。本题可转化为 求斐波那契数列第 n 项的值 ,与 面试题 斐波那契数列 等价,唯一的不同在于起始数字不同。
青蛙跳台阶问题: f(0)=1f(0)=1 , f(1)=1f(1)=1 , f(2)=2f(2)=2 ;
斐波那契数列问题: f(0)=0f(0)=0 , f(1)=1f(1)=1 , f(2)=1f(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)O 的额外空间。
动态规划:
原理: 以斐波那契数列性质 f(n + 1) = f(n) + f(n - 1) 为转移方程。从计算效率、空间复杂度上看,动态规划是本题的最佳解法。
// 递归--超出时间限制
function getFn(n) {
if (n == 0) {
return 1
}
if (n == 1) {
return 1
}
if (n == 2) {
return 2
}
return getFn(n-1) + getFn(n-2)
}
console.log(getFn(7));
// 备忘录算法:将重复用到的项存入 --超出时间限制
// 先判断是否有该项,有就用,没有就存入该项
function getFnThreen(n) {
var obj = {}
if (n == 0) {
return 1
}
if (n == 1) {
return 1
}
if (obj.n) {
return obj.n
} else {
obj.n = getFnThreen(n-1) + getFnThreen(n-2)
return obj.n
}
}
console.log(getFnThreen(7));
// 尾调用优化 : 一个函数的返回值是另一个函数
function getFnTwo(n) {
return fibIm(1,1,n)
}
function fibIm(a, b, n) {
if ( n == 0 ) {
return a
}
return fibIm(b, (a+b), n-1)
}
console.log(getFnTwo(4));
// 动态规划
// 动态规划:边界、状态转换方程、最优子结构
// 边界:F(0)=0,F(1)=1;状态转换方程:F(n) = F(n-1) + F(n-2);
// 自底向上,每次只用保存两个变量F(n-1)、F(n-2)
function getFnFour(n) {
if (n == 0) {
return 1
}
if (n == 1) {
return 1
}
var a=1,b=2,temp
for (var i=3; i<=n; i++){
temp = a+b
a = b;
b = temp
}
return temp
}
console.log(getFnTwo(4));
var numWays = function(n) {
var arr = [1,1,2]
for (var i=3; i<=n; i++){
arr[i] = (arr[i-1] + arr[i-2])% 1000000007
}
return arr[n]
};
最后这种性能最优。