剑指Offer-递归部分

重建二叉树

输入某二叉树的前序遍历和中序遍历的结果,请重建该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。

例如,给出
前序遍历 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;
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值