递归转循环

本文探讨了如何将递归转换为循环逻辑,以避免StackOverflowError。通过实例分析了爬楼梯问题,展示了如何使用栈和循环实现递归算法的替换,并提出了优化策略,如使用哈希表存储中间值以避免重复计算。最后提到了动态规划作为更优解法。
摘要由CSDN通过智能技术生成

目的

笔者认为,递归是不可控的,业务数据难免存在错误数据,一旦没有很好的把控,递归层数激增,StackOverflowError就出现了,所以,在没有其他替代算法时,至少应该将递归转成循环逻辑

准备

所谓递归,即函数自己调用自己,最后调用的先结束,可见,它的调用顺序就是个栈—先入后出。通过手动维护一个栈结构,加上循环逻辑,那么,理论上递归就可以被取代了,事实也确实如此,所有的递归,都可以转为【栈+循环】的结构,这些可以找相应的文章查阅

示例

来个爬楼梯的例子:

假设你正在爬楼梯。需要 n 阶你才能到达楼顶,并且每次你只可以爬 1 或 2 个台阶,那么有多少种不同的方法可以爬到楼顶呢?

例如:楼梯有3阶
则有三种方法可以爬到楼顶。

  1. 1 阶 + 1 阶 + 1 阶
  2. 1 阶 + 2 阶
  3. 2 阶 + 1 阶

用递归的方式,可以很好得出答案,且简单易懂

fun climbStairs(n: Int): Int {
    if (n == 0) return 1
    if (n < 0) return 0
    val r1 = climbStairs(n - 1)
    val r2 = climbStairs(n - 2)
    return r1 + r2
}

但是,当n的数字很大时,递归是hold不住的。
那么,怎么改成循环呢?

首先我们要有个栈,用来取代方法的先入后出式调用

fun climbStairs(n: Int): Int {
    val stack: Stack<Int> = Stack()
    ...
}

至于栈的存放内容,应该是我们的台阶数为 n,即 剩余 n 个台阶时,还会有几种方式到达

fun climbStairs(n: Int): Int {
    val stack: Stack<Int> = Stack()
    stack.push(n)
    ...
}

栈有了,那还得来个循环,而循环首先要确定终止条件,这个倒是不难,由递归改造而来的循环,自然是递归调用结束,也就是栈里面没内容时

fun climbStairs(n: Int): Int {
    val stack: Stack<Int> = Stack()
    stack.push(n)
    while(!stack.isEmpty()) {
        ...
    }
    ...
}

至于循环的内容,就要根据具体业务来确定了。
本例中,首先我们需要返回值,所以要有一个循环作用域外的变量用来接收,而每次计算返回值的条件就是 n=0,即表示刚好登完台阶,多说无益,直接看代码就能理解了

fun climbStairs(n: Int): Int {
    val stack: Stack<Int> = Stack()
    stack.push(n)
    var count = 0 //保存结果
    while(!stack.isEmpty()) {
        val num = stack.pop() //每次出栈一个数据
        var n1 = num - 1 //登1级台阶时
        var n2 = num - 2 //登2级台阶时
        if (n1 == 0 || n2 == 0) count++ //登完台阶时,表示这是一种方案,结果值加1
        if (n1 > 0) stack.push(n1) //如果登完1阶后还有剩余台阶,则继续
        if (n2 > 0) stack.push(n2) //如果登完2阶后还有剩余台阶,也一样继续
    }
    return count
}

优化

可以看出,将递归转为循环后,如果n足够大,那么入栈的数据也是极大的,相对直接递归而言,无非就是将方法栈的开销转为了手工维护的栈的开销,空间复杂度或许少了些,但是时间复杂度依旧,所以,最好是能进一步优化

本例中,若n=5,则必然存在这两种方案

  1. 先走两阶,剩余三阶
  2. 走一阶,再走一阶,剩余三阶

对于这剩下的三阶,就是重复计算了,而它们的计算结果显然是可以共用的,这就引出了一种通用的优化方案:利用哈希表来存储中间值,避免重复计算

当然,本例只是具体样例,它的最优解,应是换做【动态规划】算法

  • 9
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值