剑指offer里的递归与循环

其实这是第二遍刷剑指offer,发现已经忘得差不多了…虽然说有印象,但该不会写还是不会写。果然纯粹为刷题而刷题,完全不总结还是没啥用啊。所以这次根据题号顺序对里面的算法思想做个总结。

递归怎么解

对于算法ruo ji来说真的很不愿意用递归…因为绕来绕去一定会把自己绕进去。在平时写题时能不用就不用,宁愿写个超级复杂的循环也不想用递归。
但是…!在一定场景下递归真的很简洁很容易。比如这道

根据前序中序序列重建二叉树

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

在草稿纸上画了一通,已经明确核心思想:

  1. 每次取出前序遍历序列的第一个数作为根节点,找出它在中序序列的位置,
    let index = vin.indexOf(pre[0]);
    且下一次递归的前序序列也会根据中序序列中的节点数被分成两半。一半是pre.slice(1, index + 1),一半是pre.slice(index + 1)

  2. 以这个数为分界点,将中序遍历序列分为两半。前半部分为左子树,后半部分为右子树。
    let left = vin.slice(0, index);let right = vin.slice(index + 1);

明确之后,感觉要用递归,但还是试试循环求解。若是循环,每次拆解中序序列时会非常麻烦,因为会产生很多的零碎序列片段。

所以应该使用递归,但每次递归产生的结果是什么?应该返回什么?这个是递归的重点。根据题目,既然最后的结果应该是一颗完整的数,所以递归结果即return的内容一定是一个含有val,左右节点的树节点

//返回值是一个节点,已经包括其左右子树
return {
    val: pre[0],
    left: reConstructBinaryTree(pre.slice(1, index + 1), left),
    right: reConstructBinaryTree(pre.slice(index + 1), right)
}

根据这道题,会发现其实递归的难点并不在于算法,而在于对每次递归产生的结果及返回值的把握,一定要搞清楚返回值到底是什么。

还有剑指offer里的第三题也是关于递归的,不过跟这道题思想有点不一样。

从尾到头打印链表

  • 输入一个链表,按链表值从尾到头的顺序返回一个ArrayList。

当然可以无脑循环+unshift,不过估计面试可以直接结束了

如果要用递归的话,明确下一次递归应该是这次递归头结点的next节点,但还是一直卡在应该返回什么。很容易知道如果递归到最后尾节点时可以直接返回值不用递归了。所以应该分界讨论其返回值

  • 如果不是最后节点,那这次循环得到的结果应该是一个尾节点到head.next的结果数组
  • 如果是尾节点,这才是最开始,将其直接push进数组即可。
//递归,
let res = [];
if(head) {
    if(head.next) {
        res = printListFromTailToHead(head.next);
        //若不是最后节点
        //这个函数返回的应该已经是一个从尾到头的结果数组
    }
    res.push(head.val);
    //若是最后节点
    //直接将其置加入数组
}
return res;

…还有一个神级看不懂的递归方法,求解

if(head){
    printListFromTailToHead(head.next);
    res.unshift(head.val);
}
return res;

递归产生的问题

虽然说递归看上去很厉害很万能,但有时递归会产生非常多的重复计算造成时间复杂度非常大。例如经典的Fibonacci数列。

Fibonacci数列

虽然一句return Fibonacci(n-1) + Fibonacci(n-1)就可以解决,但这样效率非常低,面试时这么写肯定是拿不到offer了

不用递归就只能用循环了,整体只用记录三个数即可

function Fibonacci(n) {
    let pre = 0, cur = 1,res;
    if(n === 0) return pre;
    if(n === 1) return cur;
    for(let i = 2; i<=n; i++) {
        res = pre + cur;
        pre = cur;
        cur = res;
    }
    return res;
}

跳台阶

  • 一只青蛙一次可以跳上1级台阶,也可以跳上2级。求该青蛙跳上一个n级的台阶总共有多少种跳法(先后次序不同算不同的结果)。

对于每次情况,不然就跳1级,不然就跳2级,只有这两种情况,且对于n级台阶跳法有f(n)种。

  1. 如果这次跳1级,还剩下n-1级台阶要跳,即跳法是f(n-1)
  2. 如果这次跳2级,还剩下n-2级台阶要跳,即跳法是f(n-2)
  3. 结合前面两种,即f(n) = f(n-1) + f(n-2),本质是一个斐波那契数列,且f(1) = 1, f(2) = 2

变态跳台阶

  • 一只青蛙一次可以跳上1级台阶,也可以跳上2级……它也可以跳上n级。求该青蛙跳上一个n级的台阶总共有多少种跳法。

与上题很像,对于每次情况:

  1. 跳1级,剩下跳法f(n-1)
  2. 跳2级,剩下跳法f(n-2)
  3. 跳3级,剩下跳法f(n-3)
  • 跳n-1级,剩下跳法f(1)
    所以f(n) = f(1) + f(2) + f(3) + … + f(n-1),且f(1) = 1, f(2) = 2
    也可以理解成,每增加1级,跳法翻倍(因为每次都前一个数加两遍
function jumpFloorII(number)
{
    // write code here
    let res = 1;
    if(number === 1) return res;
    for(let i = 2; i <=number; i++) {
        res = res*2;
    }
    return res;
}

同样可以用递归来做,这里的递归跟循环基本上一致。

function JumpFloorII(number) {
    if(target === 1) {
        return 1;
    } else {
        return 2* JumpFloorII(target);
    }
}

总结

整体来说,递归对于很多复杂的算法题非常有帮助,但在使用递归的同时需要注意其算法复杂度是否太大。并且递归对我的一个最大难点就是:

  • 递归的返回值与递归状态之间的关系

可以

  • *模拟递归到底时的情况
  • 明确每一次递归产生的结果是什么
  • 不同次数递归之间的关系
全部评论

相关推荐

01-30 09:45
燕山大学 Java
喵_coding:这种直接跑就完事了 哪有毕业了才签合同 任何offer和三方都没有的
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
正在热议
更多
# 长得好看会提高面试通过率吗? #
3876次浏览 45人参与
# 离家近房租贵VS离家远但房租低,怎么选 #
16896次浏览 137人参与
# 米连集团26产品管培生项目 #
7286次浏览 226人参与
# 春招至今,你的战绩如何? #
15716次浏览 144人参与
# 你的实习产出是真实的还是包装的? #
3051次浏览 53人参与
# 沪漂/北漂你觉得哪个更苦? #
1513次浏览 40人参与
# MiniMax求职进展汇总 #
25134次浏览 321人参与
# HR最不可信的一句话是__ #
1078次浏览 32人参与
# AI面会问哪些问题? #
935次浏览 23人参与
# 你做过最难的笔试是哪家公司 #
1228次浏览 22人参与
# AI时代,哪个岗位还有“活路” #
2814次浏览 51人参与
# 不考虑薪资和职业,你最想做什么工作呢? #
152901次浏览 889人参与
# 军工所铁饭碗 vs 互联网高薪资,你会选谁 #
8007次浏览 43人参与
# XX请雇我工作 #
51155次浏览 171人参与
# 简历第一个项目做什么 #
32148次浏览 361人参与
# 简历中的项目经历要怎么写? #
311051次浏览 4265人参与
# 投格力的你,拿到offer了吗? #
178337次浏览 891人参与
# 你最满意的offer薪资是哪家公司? #
76978次浏览 375人参与
# 当下环境,你会继续卷互联网,还是看其他行业机会 #
187585次浏览 1123人参与
# AI时代,哪些岗位最容易被淘汰 #
64730次浏览 886人参与
# 如果重来一次你还会读研吗 #
230010次浏览 2011人参与
# 正在春招的你,也参与了去年秋招吗? #
364336次浏览 2642人参与
牛客网
牛客网在线编程
牛客网题解
牛客企业服务