重建二叉树
输入某二叉树的前序遍历和中序遍历的结果,请重建该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。
例如,给出
前序遍历 preorder = [3,9,20,15,7]
中序遍历 inorder = [9,3,15,20,7]
返回如下的二叉树:
3
/ \
9 20
/ \
15 7
解题思路
首先前序/后序遍历 + 中序遍历可以重建二叉树。题目考察的就是前序+中序来重建二叉树,后序+中序的思路是类似的。
例子与思路
假设有二叉树如下:
1
/ \
2 3
/ \
4 5
它的前序遍历的顺序是:1 2 4 5 3。中序遍历的顺序是:4 2 5 1 3
因为前序遍历的第一个元素就是当前二叉树的根节点。那么,这个值就可以将中序遍历分成 2 个部分。在以上面的例子,中序遍历就被分成了 4 2 5 和 3 两个部分。4 2 5就是左子树,3就是右子树。
最后,根据左右子树,继续递归即可
var buildTree = function(preorder, inorder) {
if(!preorder.length || !inorder.length){
return null
};
let rootVal = preorder[0];
let node = new TreeNode(rootVal);
let i = 0;
for(;i<inorder.length;i++){
if(rootVal == inorder[i]){
break
}
};
node.left = buildTree(preorder.slice(1,i+1),inorder.slice(0,i));
node.right = buildTree(preorder.slice(i+1),inorder.slice(i+1));
return node
};
斐波那契数列
写一个函数,输入 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。
示例 1:
输入:n = 2
输出:1
示例 2:
输入:n = 5
输出:5
解题思路
由于直接递归深度过深会超时,采用正向思维。
var fib = function(n) {
var n1 = 0,n2 = 1,sum;
for(let i=0;i<n;i++){
sum = (n1 + n2)%1000000007;
n1 = n2;
n2 = sum;
};
return n1
};
青蛙跳台阶问题
一只青蛙一次可以跳上1级台阶,也可以跳上2级台阶。求该青蛙跳上一个 n 级的台阶总共有多少种跳法。
答案需要取模 1e9+7(1000000007),如计算初始结果为:1000000008,请返回 1。
示例 1:
输入:n = 2
输出:2
示例 2:
输入:n = 7
输出:21
示例 3:
输入:n = 0
输出:1
解题思路
青蛙的最后一步不是跳一阶就是跳两阶;假设一共10阶,如果要跳1阶的话,就是之前跳9阶,如果跳2阶的话,就是之前跳8阶,相当于斐波那契数列,但是注意,循环应该从i=1开始,以为台阶如果有0或者1阶要单独考虑,所以去除了这一组合,从1阶(有一种跳法)和2阶(有两种跳法)这一组合开始。
var numWays = function (n) {
if(n<=1) return 1;
let a = 1,b = 2;
for(let i=1;i<n;i++){
sum = (a + b)%1000000007;
a = b;
b = sum;
}
return a
};
数值的整数次方
实现 pow(x, n) ,即计算 x 的 n 次幂函数(即,xn)。不得使用库函数,同时不需要考虑大数问题。
示例 1:
输入:x = 2.00000, n = 10
输出:1024.00000
示例 2:
输入:x = 2.10000, n = 3
输出:9.26100
示例 3:
输入:x = 2.00000, n = -2
输出:0.25000
解释:2-2 = 1/22 = 1/4 = 0.25
原始想法
var myPow = function(x, n) {
let mul = 1;
if(n == 0){
return 1.00000
}else if(n < 0){
for(let i=n;i<0;i++){
mul = 1/x * mul;
}
}else{
for(let i=0;i<n;i++){
mul = x * mul
}
}
return mul
};
时间复杂度为O(n),该题会超时,故要想办法降低时间复杂度
解题思路1
二分法
var myPow = function(x, n) {
if(n == 0) return 1;
if(n == 1) return x;
if(n == -1) return 1/x;
if(n%2 == 0){
let a= myPow(x,n/2);
return a*a;
}else{
let b = myPow(x,(n-1)/2);
return b*b*x
}
};
解题思路2
位运算
可以看到,对 base 进行自乘,导致 base 的指数每次都扩大 2 倍。与 exponent 的二进制相对应。
以上图为例,整个算法的流程如下:
结果值 result 初始为 1
base 初始为 3,此时 exponent 的二进制最右位为 1,更新结果为:base * result
exponent 右移一位。base 进行累乘,base 更新为 3 的 2 次方。由于 exponent 的二进制最右位为 0,不更新结果
exponent 右移一位。base 进行累乘,base 更新为 3 的 4 次方。此时 exponent 的二进制最右位为 1,更新结果为:base * result
结束
var myPow = function(x, n) {
if (n == 0) {
return 1;
}
if (n == 1) {
return x;
}
const isNegative = n < 0; // 是否是负指数
let absn = Math.abs(n);
let result = 1;
while (absn) {
// 如果n最右位是1,将当前x累乘到result
if (absn & 1) { //ansn二进制的第一位
result = result * x;
}
x = x * x; // x自乘法
absn = Math.floor(absn / 2); // n右移1位
}
return isNegative ? 1 / result : result;