函数递归调用
看清递归每一层的栈帧压入与回溯,掌握边界条件、尾递归优化与递归到迭代的转换
按 空格/→ 演示下一步
全部页面点击任意一页,跳回舞台从这页播放
函数递归调用
看清递归每一层的栈帧压入与回溯,掌握边界条件、尾递归优化与递归到迭代的转换
递归问题开场白
你拆过俄罗斯套娃吧?最大那层里面套着一个一模一样的更小娃娃,再拆还有,直到最小的实心那一颗——打不开为止。递归,就是让函数做一模一样的事。
外层套内层=大问题含同类小问题;最小实心娃娃=基线条件
递归定义和调用过程
迷宫里找出口——每到一个岔路就选一条深入,走到死胡同退回上层重新选——这种'深入再退回'的过程,正是递归调用栈的工作方式。
打开一层又一层,最小的是基线;一层层合回去就是逐层返回结果
运行程序
上一页讲了递归的'形状'——函数调用自身。但程序真跑起来时,机器怎么记住'这一层还没算完,等子调用回来再继续'?靠的是内存里一摞叠起来的'便签',专业名叫调用栈。
新便签压顶、做完撕顶,每张记着'我做到哪、做完去哪'
汉诺塔介绍
上一页跑完程序,你看到的'A→C、B→A...'就是这古老谜题的解。汉诺塔——一个用递归天然就能漂亮回答的问题,咱们先把谜面看清。
先腾空上面小箱到临时桌 → 搬大箱去目的地 → 小箱叠回到大箱上方,每层内部又是同样三步
汉诺塔讲解
前页我们认识了汉诺塔——三根柱子、64个盘子。这页要真正弄懂它为什么是递归的经典案例,以及递归到底是怎么把难题拆开的。
小箱先一个个挪开让路,大箱才能一次到位——递归就是让小问题先解决
汉诺塔程序运行
上页讲了汉诺塔的拆分思路:把 n 块分成「上面 n-1 块」和「最底 1 块」。现在的问题是——翻译成代码后,程序到底是怎么一步步跑出正确顺序的?
处理当前事被打断就先记下来(压栈),回头看清单顶端(弹栈)
递归调用例题
前面我们跟着汉诺塔走完了一次完整递归。现在换四个更小的例题,把「拆成同形子问题」的套路在不同场景里再练一遍。
总包把任务拆给分包,分包再拆,直到有人能直接做;结果再层层回传汇总
递归总结
从汉诺塔和例题的运行过程,我们已经完整看到了递归是怎么'进去'和'出来'的。现在把这一路走过的关键点串起来:递归到底在干什么?
每开一个套娃(递推入栈)都把当前状态装进去,直到最小的那个(基例),再一个个合上(归回出栈)
本节要点
- ✓终止条件(基例)是递归能停下来的唯一保险
- ✓每层调用都是独立栈帧,参数与局部变量各自一份
- ✓递归前语句递下去执行,递归后语句归上来执行
- ✓递归深度受栈容量硬性限制,过深即溢出
- ✓设计递归等于写数学归纳法:证基例 + 证递推
课后思考
三个问题带走琢磨。先自己思考,再看参考答案——重要的不是答对,而是想清楚为什么。
参考答案没有终止条件 = 无限递归:每次调用都在栈上压入新帧,直到栈空间耗尽、程序崩溃(栈溢出)。基例就是递归的『刹车』。
参考答案递归胜在贴合『自相似』结构——树的遍历、分治、汉诺塔、排列组合,递归往往几行说清;迭代要手动维护栈/队列,逻辑繁琐得多。
参考答案都能——本质是把调用栈换成显式栈。但 Python 默认递归上限约 1000 层,深递归会抛 RecursionError,这是『递归不万能』的现实边界。