You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

尾递归如何改变算法的大O复杂度?

用Scheme的my-length理解递归尾优化

让我们通过计算列表长度的例子,直观对比未做尾优化和做了尾优化的递归实现差异,搞清楚尾优化到底解决了什么问题。

未进行尾优化的实现

先看这个最直观的递归版本:

(define (my-length lst)
  (cond [(empty? lst) 0]
        [else (+ 1 (my-length (rest lst)))]))

它的计算过程是层层展开的,每一次递归调用后都得等着结果回来才能执行+1操作:

(my-length (list "a" "b" "c")) 
= (+ 1 (my-length (list "b" "c"))) 
= (+ 1 (+ 1 (my-length (list "c")))) 
= (+ 1 (+ 1 (+ 1 (my-length (list))))) 
= (+ 1 (+ 1 (+ 1 0))) 
= (+ 1 (+ 1 1)) 
= (+ 1 2) 
= 3

这个版本的问题在于:每一层递归都需要在调用栈里保留一个栈帧,等着下一层的计算结果返回后完成+1。如果列表特别长,调用栈会越来越深,最终可能导致栈溢出。

尾优化后的实现

尾优化的核心思路是把中间计算结果作为参数传递给递归函数,让递归调用成为函数的最后一个操作,这样解释器就可以重用当前的栈帧,不用额外占用空间。

优化后的代码是这样的:

(define (my-length lst)
  ; 定义局部迭代函数,负责携带当前已计算的长度
  (define (iter lst len)
    (cond [(empty? lst) len]
          [else (iter (rest lst) (+ len 1))]))
  ; 初始调用时,已计算长度为0
  (iter lst 0))

它的计算过程就简洁多了,每一步都是直接进入下一次递归,没有后续的等待操作:

(my-length (list "a" "b" "c"))
= (iter (list "a" "b" "c") 0)
= (iter (list "b" "c") 1)
= (iter (list "c") 2)
= (iter '() 3)
= 3

这里的iter函数每次递归调用都是整个函数的最后一步操作(尾调用),Scheme解释器会识别这种模式,直接复用当前栈帧,不管列表多长,栈的占用量都是恒定的,彻底避免了栈溢出的风险。

内容的提问来源于stack exchange,提问作者HappyFace

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.25 06:38:05