首页/新闻资讯/正文详情

汉诺塔递归与非递归实现:分治、调用栈与显式栈迭代

发布时间:2026/11/13 17:20:39 来源:云帆数科 栏目:资讯中心
汉诺塔递归与非递归实现:分治、调用栈与显式栈迭代
1. 为什么这道题能几十年稳坐递归教学的C位第一次在课上听到“汉罗塔”这三个字我脑子里冒出来的是一个地名后来才反应过来它就是那位法国数学家整理并推广的“汉诺塔”中文里音译被写串了几次两种写法在教材和论坛上都还在流通。三根柱子、一摞从大到小叠好的圆盘规则简单到一句话说完一次只能搬一个盘大盘不能压在小盘上把所有盘从起始柱搬到目标柱。可就是这么个木头玩具我后来在面试复盘、在代码评审、在给新人讲递归和调用栈的时候一次又一次把它翻出来当例子。它把“分治”“状态转移”“指数级复杂度”“栈帧压入弹出”这几件本来很抽象的事压缩进了三根柱子里你能用手推也能用代码跑还能用肉眼数出步数对不对。这篇内容想解决的问题很具体很多人背得出那句“先把上面 n-1 个盘移到辅助柱”但真让他从零推一遍状态转移方程就卡住让他写个非递归版本更是直接懵还有人代码写出来了n 设成 64 就跑出一堆溢出或者卡死不知道坑在哪。我会从递归思路的完整推导讲起把 2^n - 1 这个步数公式拆开算给你看然后落到 Python、C、JavaScript 三种语言的实现细节再往下延伸到显式栈模拟和位运算迭代两条非递归路线最后补充四柱变体、相邻移动变体和一整套排查清单。刚接触递归的同学可以顺着第 2 章一步步走写过但被边界坑过的朋友直接跳到第 3 章第 3 节和第 6 章会更有收获那里全是我和别人踩过的真实痕迹。1.1 三根柱子撑起的三个技术领域很多人把汉诺塔当成一道“练习题”做完就扔其实它同时踩在三个领域上。第一是算法设计与递归分治它是最干净的可分解问题任何规模 n 的解都能由两个规模 n-1 的同构解加一次移动拼出来没有冗余分支没有剪枝空间特别适合用来观察“分解—求解—合并”这条主线到底长什么样。第二是程序运行时与调用栈递归函数的每一次调用都会压入一个栈帧汉诺塔的递归树是一棵满二叉树深度等于 n节点数等于 2^n - 1你写 n30实际上就是在让运行时为接近十亿次函数调用做准备这时候爆栈、超时、内存吃掉多少全是能实测的。第三是复杂度与数值边界步数按指数增长n 每加一工作量翻倍还多这直接牵扯到整数类型的位宽、浮点精度、语言里的移位运算行为是一堂很实在的“数值安全”课。我之所以愿意反复讲这道题是因为它把“正确性”和“可行性”这两件事摆在了同一张桌子上。递推式能证明算法正确但正确不等于跑得完64 片盘的传说版本听起来浪漫实际算下来人类根本等不到那天。这种“理论上成立、工程上不可行”的落差恰恰是新手最需要建立的直觉——写代码不是解数学题你得看输入规模落在哪个区间再决定用什么数据结构、什么类型、什么策略。1.2 这道题真正的难点不在递归而在“信任”我带过不少人卡住的地方几乎一模一样他们能看懂hanoi(n-1, from, to, via)这行代码的每一个字符但不敢信它。心里想的是“这 n-1 个盘移过去的过程中会不会把局面搞乱万一后面那块大盘没地方放怎么办”这种怀疑非常正常因为人的大脑习惯逐步跟踪而递归要求你相信子问题已经被正确解决把注意力只放在“当前这一层做了什么”上。汉诺塔的价值就在这儿它强迫你练习这种信任而且练完之后你能用 n1、n2、n3 手工验算回来把信任变成可验证的事实。所以后面第 2 章我不会直接甩代码而是先把 n1 到 n3 的每一步写在纸上让你亲眼看到“子问题解完之后局面恰好恢复成一个干净的三柱状态”这才是递归能成立的物理基础。搞不清这一点抄十遍代码也没用。1.3 这篇内容适合哪些人看如果你正在学第一门编程语言刚接触函数调用那第 2 章和第 3 章第 1 节是为你准备的跟着敲一遍把打印出来的移动序列和手推结果对一遍比看十页教材管用。如果你已经能默写递归版但被面试官追问“不用递归怎么写”“n 很大怎么办”“四根柱子呢”第 4 章和第 5 章能把这些延伸问题补上。如果你是带项目的人想找个小例子给团队讲清楚调用栈和指数爆炸的风险第 4 章第 1 节的显式栈模拟可以直接拿去当教学素材——把系统隐式做的事摊开在数组里比任何示意图都直观。文中不会假定你熟悉某个特定框架代码都是独立可运行的片段。2. 递归思路的完整推导从手动搬运到状态转移方程2.1 先别写代码拿三张纸片推一遍 n1、2、3我建议所有人在动手写之前先找三张纸片代表柱子撕几块大小不同的纸当圆盘或者干脆用三个杯子和几枚硬币。n1 的时候没什么好说的直接从起始柱搬到目标柱一步。n2 的时候先把小盘搬到辅助柱1 步再把大盘搬到目标柱1 步最后把小盘从辅助柱搬到目标柱1 步共 3 步。这三步里最关键的是第一步你明知道小盘最终要去目标柱却必须先把它放到辅助柱去。这个“看起来在绕路”的动作是整个递归的核心它腾出了目标柱的空间给大盘。n3 的时候思路要再抬一层。你先把上面两个盘当成一个整体借助目标柱整体搬到辅助柱去——这一步内部需要 3 次移动然后把最底下的大盘从起始柱搬到目标柱——1 次最后把辅助柱上那两个盘整体搬到目标柱——又 3 次。总共 7 次。手推的时候拿笔记一下每次移动后的三柱状态你会发现中间那个“大盘落位”的瞬间局面非常干净起始柱空了大盘已经到位剩下两个盘全在辅助柱上问题退化成了一个规模为 2 的原问题只不过柱子的角色换了名。这个观察就是全部秘密。任何规模 n 的局面都能在“大盘落位”这一刻退化成规模 n-1 的同构局面只是起始柱、辅助柱、目标柱的名字轮换了一遍。你不用关心 n-1 那部分内部怎么折腾只要保证它折腾完、局面是干净的剩下的就是重复。2.2 递归三要素在汉诺塔上的逐条对照教科书讲递归三要素终止条件、递归调用、当前层逻辑。放到这道题上三者对应得非常整齐。终止条件是 n 1只剩一个盘就直接搬这时候不需要任何辅助柱参与一次移动搞定。递归调用有两次位置在“当前层逻辑”的一前一后先把 n-1 个盘从起始柱搬到辅助柱目标柱临时当辅助用再把 n-1 个盘从辅助柱搬到目标柱起始柱临时当辅助用。当前层逻辑只有一条把编号最大的那个盘从起始柱搬到目标柱。参数顺序是新手最容易写错的地方我把它固化成一个记忆法函数签名hanoi(n, source, aux, target)第一次递归调用传(n-1, source, target, aux)第二次传(n-1, aux, source, target)。记不住就念一遍“从 source 搬 n-1 个到 auxtarget 做中转再从 aux 搬 n-1 个到 targetsource 做中转。”念两次手就记住了。这里的顺序错一位程序不会报错只会打印出一堆违反“大盘压小盘”规则的序列属于典型静默错误非常值得警惕。提示如果你写完之后不确定对不对别靠眼睛看写个校验函数。维护三个列表当柱子每执行一次移动就检查被压入的盘是否小于栈顶盘同时统计总步数是否等于 2^n - 1。这两个断言能挡住九成以上的参数顺序错误。2.3 2^n - 1 是怎么算出来的以及 64 片盘意味着什么设 T(n) 为搬完 n 个盘的最少步数。按上面的分解T(n) T(n-1) 1 T(n-1) 2T(n-1) 1边界 T(1) 1。展开这个递推T(n) 2T(n-1) 1 4T(n-2) 2 1 ... 2^(n-1)·T(1) (2^(n-1) - 1) 2^n - 1。你也可以用“加一配凑”的技巧两边同加 1 得 T(n) 1 2(T(n-1) 1)于是 T(n) 1 2^(n-1)·(T(1) 1) 2^n直接得到 T(n) 2^n - 1。这个技巧在处理形如 T(n) aT(n-1) b 的递推时特别好用值得记下来。最少性也要说一句为什么不可能更少因为最大的盘迟早要动一次而它要动上面 n-1 个盘必须全部在另一根柱子上这至少要 T(n-1) 步大盘动完之后那 n-1 个盘还要再整体搬到目标柱又要 T(n-1) 步。所以 2T(n-1) 1 是下界而我们的构造恰好达到下界因此是最优。再看 64 片盘的数字2^64 - 1 18,446,744,073,709,551,615约 1.8×10^19 步。假设每秒搬一次、不眠不休一年约 3.15×10^7 秒除一下得 5.85×10^11 年也就是接近六千亿年。这个数字不必背但那种量级感要建立起来指数增长不是“慢一点”是“换一个宇宙尺度”。我实际做过一个小实验让程序每秒打印一万次移动跑 n32 大约需要 4.3×10^9 次移动折合 43 万秒五个昼夜。n 只加了 8时间就从“instant”变成了“一周”这就是为什么后面讲实现时必须先谈输入规模。3. 三种语言落地代码、参数与那些藏得很深的坑3.1 Python 版本最短的实现最容易被忽略的递归深度Python 写这题极短四行核心逻辑import sys def hanoi(n, source, aux, target, moves): if n 1: moves.append((source, target)) return hanoi(n - 1, source, target, aux, moves) moves.append((source, target)) hanoi(n - 1, aux, source, target, moves) return moves if __name__ __main__: sys.setrecursionlimit(10000) result hanoi(10, A, B, C, []) print(len(result)) # 必须是 1023 print(result[:3])这里的moves用列表收集而不是直接 print是我个人强烈建议的做法。原因有两个一是方便做断言校验len(moves) 2**n - 1顺手就能验二是把“计算”和“输出”解耦你后面要改成写文件、要改成 GUI 动画、要改成统计每根柱子的利用率都不用动核心逻辑。很多初学者在递归里直接 print一旦发现结果不对就没法回溯只能加一堆日志越加越乱。递归深度这个坑必须点出来。Python 默认递归上限是 1000汉诺塔的调用深度恰好是 n所以 n 超过 990 左右就会抛RecursionError: maximum recursion depth exceeded。有人以为sys.setrecursionlimit调大就万事大吉实际上它只是把限制提高真的压到几千层时仍有触发解释器栈崩溃的风险而且就算不崩C 栈上的开销也不小。我的做法是n 小于 20 用递归n 大于等于 20 一律走第 4 章的迭代法因为 n20 已经是百万级步数递归那点优雅完全没有意义。3.2 C 语言版本类型位宽和格式化符的连环陷阱C 版本我一般写成计步模式因为要真打印 2^30 行终端先崩#include stdio.h unsigned long long g_count 0; void hanoi(int n, char src, char aux, char dst) { if (n 1) { g_count; return; } hanoi(n - 1, src, dst, aux); g_count; hanoi(n - 1, aux, src, dst); } int main(void) { int n 40; hanoi(n, A, B, C); printf(%llu\n, g_count); /* 1099511627775 */ return 0; }第一个坑是类型。步数按 2^n 增长用int只能撑到 n302^30 - 1 1,073,741,823 接近 int 上限 2,147,483,647其实 n31 也还行n32 就溢出成负数或 0。unsigned long long至少 64 位能撑到 n63n64 时 2^64 已经无法表示1ULL 64在 C 里是未定义行为不同编译器给出的结果可能完全不同有的给 0有的给 1。所以想算 n64就别硬用整数了改用 Python 或者字符串大数。第二个坑是格式化符。unsigned long long要用%llu在部分老版编译器的运行时里得写%I64u写错不会编译报错只会打印出乱码或者 0。我见过有人因为这个在群里折腾半宿最后发现是格式化符不匹配。第三个坑是递归的栈开销。C 的栈帧比 Python 小得多但默认线程栈也有几 MBn 到几万层时照样会踩穿。工程里我倾向于把递归深度上限写成常量并在入口处校验超了就拒绝别让程序在中间某个不确定的位置崩掉。3.3 JavaScript 版本移位的 32 位陷阱和大数类型的选择JS 在浏览器里跑这题最隐蔽的坑是位运算。1 n里的是 32 位有符号操作n 到 31 就开始出问题n32 时1 32等于 1 而不是 4294967296。想算步数老老实实用Math.pow(2, n) - 1或者干脆上BigIntfunction hanoiSteps(n) { if (n 0 || !Number.isInteger(n)) throw new Error(n 必须是非负整数); if (n 1024) throw new Error(规模过大建议改用字符串大数实现); return (1n BigInt(n)) - 1n; } function hanoi(n, src, aux, dst, out []) { if (n 1) { out.push(${src}-${dst}); return out; } hanoi(n - 1, src, dst, aux, out); out.push(${src}-${dst}); hanoi(n - 1, aux, src, dst, out); return out; } console.log(hanoiSteps(64).toString()); // 18446744073709551615 console.log(hanoi(3, A, B, C).length); // 7BigInt和Number不能混着算1n 1会直接抛TypeError这一点从别的语言转过来的人极容易忘。另外 Node 环境的默认栈深度和浏览器不同同一个 n 在一边能跑、另一边报RangeError: Maximum call stack size exceeded属于很常见的“在我机器上是好的”排查时先看这个。三种语言的关键差异我整理成一张表方便对照着选语言步数整数类型能安全算到的 n递归默认深度主要坑点Python任意精度 int只受内存限制约 1000 层递归上限低需显式调整C / Cunsigned long long63视栈大小通常数万层移位未定义行为、格式化符JavaScriptNumber 或 BigIntNumber 安全到 53 位BigInt 更高视引擎通常一万层左右是 32 位、Number 与 BigInt 不可混算Javalong 或 BigIntegerlong 到 62视栈大小无符号支持弱长期用 BigInteger注意不管哪种语言先校验 n。n 为负数会直接打爆递归边界n 为非整数会无限递归n 特别大则跑不完。入口处一行守卫能省掉无数诡异的调试时间。4. 不用递归也能解显式栈与位运算两条路4.1 用数组模拟系统栈把隐式的东西摊开看递归的本质是系统帮你维护一个栈。既然如此我们完全可以自己建一个栈把“待办事项”显式压进去然后循环弹出处理这样既能规避递归深度限制又能让过程完全可见。思路是把一次调用拆成三个阶段用状态位标记def hanoi_iterative(n): moves [] # 栈元素: (规模, 起始柱, 辅助柱, 目标柱, 阶段) stack [(n, A, B, C, 0)] while stack: m, src, aux, dst, phase stack.pop() if m 0: continue if m 1: moves.append((src, dst)) continue if phase 0: # 尚未完成前半部分先登记“回来做大动作”再压入前半段 stack.append((m, src, aux, dst, 1)) stack.append((m - 1, src, dst, aux, 0)) else: # 前半部分已完成做大动作然后压入后半段 moves.append((src, dst)) stack.append((m - 1, aux, src, dst, 0)) return moves if __name__ __main__: res hanoi_iterative(20) assert len(res) 2**20 - 1 print(res[:5])顺序上有个关键点栈是后进先出的所以你要先压入“后半段的大动作”再压入“前半段的子任务”否则执行顺序会整个反过来。这个顺序我一开始写反过跑出来的结果是从目标柱往起始柱搬看起来居然也“合法”只是方向反了特别容易蒙混过关。所以务必用len(moves) 2**n - 1加规则校验双保险。这个写法还有个额外好处栈里元素的个数等于调用深度你在循环里打印len(stack)就能直观看到深度的变化曲线。n5 时深度最多 6 层一开始增长中间保持末尾回落形状很说明问题。4.2 位运算迭代法盯住最小盘每两步走一次另一种完全不同的思路不模拟栈而是直接从解的规律出发。三柱问题的移动序列有一个漂亮性质除最小盘以外的盘在任何两步之间只有唯一一种合法移动而最小盘则是每隔一步动一次且方向固定循环。于是算法变成奇数步移动最小盘偶数步做那个唯一合法的移动交替进行一共 2^n - 1 步。def hanoi_bitwise(n): pegs {A: list(range(n, 0, -1)), B: [], C: []} # n 为奇数时最小盘沿 A-C-B 循环偶数时沿 A-B-C 循环 cycle [A, C, B] if n % 2 1 else [A, B, C] pos 0 moves [] total (1 n) - 1 def top(p): return pegs[p][-1] if pegs[p] else float(inf) for step in range(1, total 1): if step % 2 1: src, dst cycle[pos], cycle[(pos 1) % 3] pos (pos 1) % 3 else: # 在除最小盘所在柱外的两根柱之间找唯一合法移动 smallest_peg cycle[(pos - 1) % 3] others [p for p in (A, B, C) if p ! smallest_peg] x, y others if top(x) top(y): src, dst x, y else: src, dst y, x pegs[dst].append(pegs[src].pop()) moves.append((src, dst)) return moves这段代码有两个细节值得说明。float(inf)当作空柱的哨兵值是为了让“小盘永远可以放到空柱上”这个判断不需要单独分支比较逻辑能统一。cycle[(pos - 1) % 3]这个回退索引是因为最小盘动完之后pos已经前移需要退一格才能拿到它现在的柱子。这类索引运算在 Python 里负数取模会自动回绕在 C 或 Java 里就得手动加 3 再取模跨语言移植时极易翻车。至于“第 k 步移动哪个盘”的判定还有个更极客的写法第 k 步移动的盘子编号等于 k 的二进制最低有效位的位置从 1 开始计。例如 k1 二进制是 1最低位 1 在第 1 位移动最小的盘k2 二进制 10最低位 1 在第 2 位移动第二小的盘k6 二进制 110最低位在第 2 位移动第二小的盘。这个性质我一开始不信写了个脚本跑了 n6 的全部 63 步逐步核对确实完全吻合。它的价值不在于实用而在于把一个算法问题和二进制表示连了起来理解了这一层你对“分治”的直觉会明显变强。4.3 两种非递归路线的取舍显式栈的好处是通用任何能写成递归的算法都能这么改而且过程可控、可暂停、可单步缺点是代码长状态位容易搞混而且仍然需要 O(n) 的额外空间。位运算迭代法的好处是极致紧凑、无额外栈空间、速度最快缺点是规则性强依赖“三柱标准题”的假设稍微改一下规则比如四柱、或者限制只能相邻移动这套规律就全废了得重新推。实测数据也分享一下在普通笔记本上跑 n24约 1677 万步纯计步不打印的情况下Python 递归版因为函数调用开销大概要十几秒显式栈版接近但略慢位运算版最快。这个对比说明一件事递归的性能损失主要来自函数调用本身而不是“递归”这个思想。所以真要优化方向是消除调用不是否定分治。方案时间复杂度额外空间可读性适用场景直接递归O(2^n)O(n) 调用栈最高教学、n 小的打印显式栈模拟O(2^n)O(n) 堆上数组中等需要单步调试、深度受限环境位运算迭代O(2^n)O(n) 三柱状态较低追求速度、批量计步直接公式O(1)O(1)最高只问步数不问路径5. 变体与延伸从三柱走出去之后5.1 四柱汉诺塔多一根柱子难度反而上一档规则不变但柱子的数量从三根变成四根。这时候“把 n-1 个盘整体搬走”的策略不再最优因为你可以利用多出来的那根柱子把一部分盘先挪到第四根柱子上腾出空间后剩下的一部分基本退回三柱玩法。经典的分解方式是先把上面 k 个盘借助全部四根柱子搬到某根暂存柱步数 T(k)再把剩下的 n-k 个盘用三柱方式搬到目标柱步数 2^(n-k) - 1因为此时暂存柱占着一根只剩三根可用最后把那 k 个盘搬到目标柱又是 T(k)。于是 T(n) min over 1≤kn of { 2·T(k) 2^(n-k) - 1 }。手算几个值T(1)1T(2)3T(3)5T(4)9T(5)13T(6)17T(7)25。对比三柱的 1、3、7、15、31、63、127差距在 n 变大后急剧拉开。这个“取最小”的搜索步骤说明这件事多一根柱子并不会让问题更简单而是把单一的确定性结构变成了需要在多个切分点上做选择的优化问题。顺带一提这个递推给出的到底是不是最优解在数学上长期是个开放问题目前只是公认的最佳方案这个细节挺值得玩味——一行代码背后站着一个没被完全证明的猜想。5.2 只能移到相邻柱子的变体另一个常见变体是三根柱子排成一条线规定每次只能把盘移到相邻的柱子上不能从最左直接跳到最右。这个版本的最少步数递推是 T(n) 3T(n-1) 2边界 T(1) 2解得 T(n) 3^n - 1。n1 时 2 步n2 时 8 步n3 时 26 步。增长速度从 2^n 变成了 3^n同样的 n差距被放大到 1.5^n 倍n20 时就是三千多倍的差别。我自己手推 n2 时足足错了三次原因在于下意识地把“整体搬运”的中间过程想简单了把两个盘从左搬到右、且只能相邻移动实际要 8 步比直觉的 4 到 5 步多。这个变体特别适合拿来练“先写递推、再验证小规模”的习惯因为它反直觉光靠脑子想很容易漏步。5.3 这些模型在真实系统里的影子抛开玩具属性这个结构在工程里到处是影子。最典型的是批量数据迁移把一组有依赖关系的资源从旧环境搬到新环境受限于同一时刻只能有一份副本落地的容量约束就像“大盘不能压小盘”最小代价的迁移顺序本质上就是汉诺塔式的分解。再比如备份轮换三盘磁带轮换任何时刻只能保持一份完整快照切换顺序的约束和柱子约束同构。还有依赖任务的调度一堆任务有先后和容量限制要在两个资源池之间倒腾分解思路通用的。我不建议把这种类比说得太具体容易显得牵强。更有价值的迁移是思维方式遇到一个规模为 n 的问题时先问“能不能把它拆成两个规模 n-1 的同构问题加一个原子操作”如果能递归结构就是现成的再看这个拆分会带来多少次重复计算是否需要用记忆化或者换成迭代。汉诺塔的两次递归调用是完全独立的、不重叠的所以它天然不需要记忆化这也解释了为什么它的复杂度就是实打实的 2^n - 1没有任何优化空间。6. 常见问题与排查技巧实录6.1 常见问题速查表我把这些年自己和别人遇到的问题汇总成表遇到现象直接对号入座现象大概率原因排查与修复打印的移动序列违反“大盘压小盘”递归调用的三个柱子参数顺序写错逐行核对source/aux/target的轮换写校验函数断言步数不是 2^n - 1边界条件写成 n 0 却没处理或多算一次把n 1作为唯一终止分支其余交给递归RecursionError或栈溢出n 超过语言默认递归深度换显式栈版本或把 n 降下来分批跑步数打印成 0 或负数整数类型位宽不够用 64 位无符号类型或 BigInt检查格式化符JS 里1 32结果不对位运算是 32 位有符号改用Math.pow或BigInt程序卡死但 CPU 不高在递归里直接大量打印I/O 阻塞先计步不打印确认算法无误再输出同一份代码换台机器就崩不同环境默认栈深度不同不依赖默认值显式限制 n 或改迭代6.2 三个我真实踩过的坑第一个坑是在递归里做全局计数却忘了初始化。我在模块顶层写了个计数器跑完 n5 得到 31高兴了半天结果紧接着跑第二个用例数字是 62才发现变量没重置。后来养成习惯计数一律通过参数或者返回列表长度来做拒绝全局可变状态。第二个坑是拿 n64 去测试性能。写完之后顺手把 n 改成 64想看传说中“搬完就世界末日”的效果结果程序直接进入无限等待我以为死循环了查了半天发现是逻辑没写错就是真的跑不完。这件事后来成了我讲复杂度时最好的例子看起来只改了一个数字实际改的是整个问题的量级。现在的习惯是任何指数级算法在运行前先估算步数超过一千万就改成计步模式或加进度输出。第三个坑是用浮点数表示步数。有次图省事用 double 存步数n53 之后就不精确了因为双精度只有 53 位有效位。步数显示成 9007199254740992 这种明显不对的数我一开始以为是逻辑错误查了很久才意识到是精度问题。计数类的东西永远用整数类型多大都不嫌大。6.3 一份可以直接抄的最小验证清单每次写完我会跑这么一套检查五秒内就能判断实现对不对n1 时输出 1 条且是起始柱到目标柱n2 时输出 3 条且第一条是起始柱到辅助柱n3 时输出 7 条第 4 条中间那条一定是起始柱到目标柱也就是最大的盘那一次移动n10 时总条数必须是 1023并且执行过程中任意时刻三根柱子内部的盘都保持从上到下递增。这几条断言写下来不到十行能挡住参数写反、边界漏判、计数重置这三类最常见的错误。最后一个经验如果要把这个算法讲给别人听或者写成文档别从代码开始。先让对方用手推 n3推到他说出“中间那步大盘移位之后局面又变成一个干净的 n-1 问题”这句话再给他看代码。顺序反了他就算看懂了每一行也没真正拿到那个能迁移到别的问题上的思维模型。我自己带新人的时候就是这么干的效果比讲一百遍调用栈都实在。

相关推荐

Hugging Face:AI开源社区与机器学习核心技术解析
Hugging Face:AI开源社区与机器学习核心技术解析

1. Hugging Face:AI领域的GitHub如何重塑机器学习生态2016年诞生于纽约的Hugging Face,最初只是个开发聊天机器人的创业公司。如今这个戴着耳机笑脸Logo的平台,已成为全球最大的开源机器学习社区,托管着超过200万个预训练模型、3万… · 2026/11/6 4:49:16

Java双亲委派模型原理与打破方案详解
Java双亲委派模型原理与打破方案详解

1. 双亲委派模型的核心机制双亲委派模型是Java类加载的基础架构,其核心工作流程可以概括为三个关键步骤:委派检查:当类加载请求到来时,子加载器不会立即尝试加载,而是先将请求委派给父加载器层级尝试:这个委… · 2026/11/6 1:19:09

中文语音识别毕设全攻略:特征提取、模型训练与问题排查
中文语音识别毕设全攻略:特征提取、模型训练与问题排查

简介:面向计算机专业毕业设计与项目实战场景,这套基于深度学习的 Python 中文语音识别系统源码包含完整的数据处理、声学模型与语言模型模块,难度适中,既适合本科毕业设计参考,也可用于课程大作业或语音识别入门实践。… · 2026/11/8 9:58:22

长汀县电工证怎么考?通过率揭秘,外省证书转过来全靠这几点
长汀县电工证怎么考?通过率揭秘,外省证书转过来全靠这几点

长汀县电工证怎么考?通过率揭秘,外省证书转过来全靠这几点 之前考的证在外省能不能转过来?这是很多在外地打工、回老家发展,又想从事电工工作的朋友最关心的问题。如果你在长汀县打算报考电工证,又担心自己之前在别处考的证是否还能用,那就得仔细看看这… · 2026/11/13 17:20:29

南网低压电工证杨老师官方回应:怕考不过白交培训费?实操经验全在这
南网低压电工证杨老师官方回应:怕考不过白交培训费?实操经验全在这

南网低压电工证杨老师官方回应:怕考不过白交培训费?实操经验全在这 怕考不过白交培训费?别再被培训机构忽悠了,南网低压电工证杨老师官方回应来了,告诉你怎么避坑!作为洛阳涧西区项目现场的管理员,我见过太多人因为没考到低压电工证,被项目停工、被罚… · 2026/11/13 17:20:29

高处电工作业安全措施考前押题怎么准备?证书过期了不知道怎么复审延期
高处电工作业安全措施考前押题怎么准备?证书过期了不知道怎么复审延期

高处电工作业安全措施考前押题怎么准备?证书过期了不知道怎么复审延期 证书过期了不知道怎么复审延期?你不是一个人。尤其是像郑州公路工程这类需要频繁高空作业的行业,高处电工作业安全措施直接关系到人身安全与工程进度,而证书一旦过期,不仅影响工作,… · 2026/11/13 17:20:29

高空特种作业架子工证在哪考怕考不过白交培训费?一文讲透报考全攻略
高空特种作业架子工证在哪考怕考不过白交培训费?一文讲透报考全攻略

高空特种作业架子工证在哪考怕考不过白交培训费?一文讲透报考全攻略 怕考不过白交培训费?别急,我来帮你避坑!最近不少开封房建工程的同行都在问: 高空特种作业架子工证在哪考… · 2026/11/13 17:20:29

有限空间作业证分哪些 郑州报考避坑指南
有限空间作业证分哪些 郑州报考避坑指南

有限空间作业证分哪些 郑州报考避坑指南 实操考试心里没底怕挂科,这是很多准备报考有限空间作业证的朋友的共同心声。别急,这期咱们就来聊聊【有限空间作业证分哪些】,顺便分享一份 郑州报考避坑指南 ,帮你少走弯路、提高通过率。… · 2026/11/13 17:20:29

齐齐哈尔初级电工证网上查询怎么弄?报名别被中介坑了
齐齐哈尔初级电工证网上查询怎么弄?报名别被中介坑了

齐齐哈尔初级电工证网上查询怎么弄?报名别被中介坑了 你是不是正为齐齐哈尔初级电工证的事发愁?网上一搜一堆信息,不知道哪个靠谱,生怕一不小心就被中介骗了钱还拿不到证。别慌,这文里我来帮你理清楚报名、考试、备考的全流程,尤其是 网上查询… · 2026/11/13 17:19:29

电工证丢失还能补吗?千万别踩坑,丢了证等于失业
电工证丢失还能补吗?千万别踩坑,丢了证等于失业

电工证丢失还能补吗?千万别踩坑,丢了证等于失业 你正在找工作,但手里的电工证突然丢了,这下怎么办?别急,先别慌, 电工证丢失还能补吗 ?答案是肯定的,但 千万别踩坑 ,补办流程如果不了解,可能会耽误你上岗、甚至影响到你的收入。… · 2026/11/12 14:09:01

电力电缆作业证直播报名入口与办理流程良心建议
电力电缆作业证直播报名入口与办理流程良心建议

电力电缆作业证直播报名入口与办理流程良心建议 你是不是也担心学历不高报不了名?别急,今天就给你讲讲 电力电缆作业证直播 的报名流程和那些你必须知道的干货, 良心建议 在这里,别错过。 电力电缆作业证直播报名需要什么条件?… · 2026/11/13 5:40:45

英德电工证报考费多少钱,怕考不过白交培训费?看完这篇别再踩坑
英德电工证报考费多少钱,怕考不过白交培训费?看完这篇别再踩坑

英德电工证报考费多少钱,怕考不过白交培训费?看完这篇别再踩坑 怕考不过白交培训费?英德电工证报考费多少钱,你真的了解吗?现在市面上培训机构五花八门,价格参差不齐,很多人一不小心就花了冤枉钱,结果还考不过。别急,今天就来给你说清楚英德电工证的… · 2026/11/12 23:27:43

维修电公用考电工证吗 在哪考最靠谱
维修电公用考电工证吗 在哪考最靠谱

维修电公用考电工证吗 在哪考最靠谱 工地太忙根本没时间复习考试,你是维修电工,天天在项目上跑,连抽个空背个题都难。那问题来了—— 维修电公用考电工证吗 ?答案是: 必须考 。尤其是在当前施工行业,没有电工证,连进场都可能被拦下。而且,… · 2026/11/13 0:00:08

电工证是不是可以登高?含金量和证书范围你必须知道的事
电工证是不是可以登高?含金量和证书范围你必须知道的事

电工证是不是可以登高?含金量和证书范围你必须知道的事 证书过期了不知道怎么复审延期,这事儿很多人遇到过,尤其是像你这样在郑州金水区做劳务班组负责人,手里握着几十个工人的证书,稍微一疏忽,就可能影响工程进度和公司信誉。特别是电工证是不是可以登… · 2026/11/13 0:00:08

鸡西电工证哪家下证快官方报名入口怎么找
鸡西电工证哪家下证快官方报名入口怎么找

鸡西电工证哪家下证快官方报名入口怎么找 你是不是也担心 学历不高怕报不上名 ?别急,今天我就来跟你说说, 鸡西电工证哪家下证快 ,还有 官方报名入口 到底怎么找,别让那些“代办”“代考”耽误了你的机会。… · 2026/11/13 0:00:08

双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试
双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/13 6:12:21

STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析
STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/13 6:11:51

DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案
DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/12 21:15:21

了解更多?预约专属演示

我们的顾问将为您一对一讲解产品与方案

企业微信二维码