我整理PTA浙大版《Python 程序设计》AC解答汇总时最想单独拎出来说一说的就是第六章。这一章的难度比前面几章明显上了一个台阶很多同学前面顺风顺水到这里突然开始反复提交、反复报错。如果你也正卡在这一章别急着怀疑自己大概率不是你代码逻辑写错了而是你还没习惯在线判题跟本地跑脚本之间的那点差别。这篇文章不会把所有题目答案平铺一遍那不解决问题我会结合第六章最常见的一类题——函数题把“做题前要想清楚什么”“哪些坑最容易踩”“踩了之后怎么排查”讲清楚。正在刷这套题的人可以参考刚学完函数、想找点OJ题练手的人也能看懂。1. 先认清第六章的题到底在考什么1.1 章节定位与难度变化浙大版教材前五章基本在讲语法基础、数据类型、流程控制。学完这些你能看懂代码能写点简单的小脚本但代码的“组织方式”还没建立起来。到了第六章重心明显从“读程序”转向“写程序”核心就是函数。这一章在教材里看起来不难概念也就那么几个定义、调用、参数、返回、作用域。可一旦换到PTA的题集里事情就变了。教材课后题只要你能在解释器里跑出结果基本就过但PTA的函数题会直接给你一个不完整的代码框架要你在限定位置补全函数而且判题程序会用多组测试数据来验证你的函数行为。这就会引出第一类困惑为什么我本地写得好好的一提交就满分不了为什么我觉得自己算对了判题就是非零返回说到底第六章不是考你会不会写“能运行的代码”而是考你能不能写对“在任意合法输入下行为都正确的代码”。这两者的差距恰恰是AC分数背后的真实含义。1.2 高频考点分布以我陆续提交的反馈来看这一章的题目大体可以归成三类每类的失分原因还不太一样。先列个表后面逐个拆。题型方向常见考查方式典型失分点基础函数定义给出主程序调用方式要求补全函数体return和print混淆函数没有返回值数值递归阶乘、斐波那契、最大公约数等经典递归递归边界缺失、深度过大导致超时数据处理函数对字符串或列表做变换返回新结果直接修改了传入的可变对象产生副作用这三种方向看起来简单但每种背后都对应一个必须理解的机制。理解了机制第六章的题目就不再是一道道孤立的题而是一类通法。2. 动手写代码前先把这三个机制吃透2.1 参数传递传入列表时到底发生了什么Python的参数传递语义严格说是“传对象引用”。很多人以为把列表传给函数就等于复制了一份列表函数内部怎么改都不影响外面。这是第六章第一个大坑。看个最典型的例子def append_one(lst): lst.append(1) nums [1, 2] append_one(nums) print(nums) # 实际输出 [1, 2, 1]函数内部对列表做append、sort、reverse这类原地操作都会直接作用于原对象。在PTA的函数题里这个特性有时候是题目故意考察的点题目要求你“返回处理后的新列表”如果你直接在原列表上操作主程序后续再使用这个列表时数据已经被污染结果自然对不上。解决办法也简单函数开头先复制一层def process(lst): temp lst[:] # 复制一份原列表不受影响 # 对 temp 做操作 return temp这里的底层原因是列表是可变对象切片生成的是新列表两者指向不同内存区域。理解到这一层类似问题就不再靠背题了。2.2 作用域函数内赋值不等于修改全局变量第二个高频问题出在作用域上。Python在函数内部给一个变量赋值默认创建的是局部变量哪怕函数外部有一个同名的全局变量。最常见的就是自增计数器这种题目count 0 def inc(): count 1 # UnboundLocalError这段代码一跑就报错原因是函数内部看到了count的赋值操作于是把count当作局部变量但此时局部变量还没初始化就直接做了加法。很多同学第一次遇到这个报错会懵明明全局变量已经定义了。解决办法要么用global声明count 0 def inc(): global count count 1要么改用返回值的形式避免修改全局变量。在OJ的封闭函数题里最好是返回新值而不是依赖全局变量因为判题程序可能会多次调用函数全局变量状态一旦没复位后面的测试点就会连环错。2.3 return和print这一步混淆直接决定AC还是WA第六章函数题里最不值得丢的分就是return和print用错。判题程序怎么知道你算对了它看的是函数的返回值不是屏幕输出。很多题目在主程序里已经写好了输出逻辑比如主代码是print(solve(n))那你的函数就必须返回结果如果你在函数内部写了print函数返回None主程序打印出None结果就是答案错误。反过来如果题目要求“直接输出结果”函数内部才需要print。判断技巧特别简单看题目的模板里有没有预留return看主调用代码怎么使用你的函数。题目描述里写“返回xxx”就return写“输出xxx”才print。这一条看着像常识但我刷第六章时见到不止一次这种提交记录本地一跑显示明明有结果一交就WA往往就是这个问题。3. 第六章函数题的AC思路三个高频题型拆解3.1 空函数框架题的补全顺序PTA的函数题经常给一个残缺模板例如def solve(n): # 请在此处实现函数很多人的第一反应是“把函数体写完就行了”但忽略了一个步骤先看主程序怎么调用。主程序可能是n int(input()) print(solve(n))也可能是n int(input()) solve(n)同样是实现一个功能前者要求返回结果后者要求函数内部自己打印。这直接决定了函数体最后一句话是return还是print。正确顺序应该是先读题目的输入输出样例再读模板代码确认函数是否需要有返回值然后才动手写实现。这样能直接规避一大类“本地对、线上错”的情况。补全函数体的时候还要注意模板里可能已经给出了参数名别自己另写一套参数名否则函数签名变了判题调用你的函数时直接报参数不匹配。3.2 递归题的边界条件与效率控制第六章涉及递归的题目比如斐波那契数列、阶乘、最大公约数。教材里大多用递归讲但直接照搬递归实现去提交会遇到两个坎。第一个坎是边界条件。很多同学能写出递归公式但忘记写终止条件或者终止条件写成了if n 1而忽略了n 0的情况。第二个坎是性能。以斐波那契为例朴素的递归写法def fib(n): if n 1: return n return fib(n-1) fib(n-2)如果测试点里有 n30、n40 这类数据运行时间会迅速拉长如果 n 再大一点还可能递归深度超限直接非零返回。原因很简单这个递归会把同一个子问题反复算好多遍复杂度接近 O(2^n)完全没必要。解法要么改成迭代def fib(n): a, b 0, 1 for _ in range(n): a, b b, a b return a要么在递归基础上加记忆化用一个字典把算过的结果存下来。PTA判题不会限制你只能用递归只要算法正确、效率够、结果对就是AC。所以遇到递归超时第一反应不是去调递归写法而是考虑能不能换成迭代这是性价比最高的改法。3.3 lambda表达式什么时候值得用部分排序、映射类的函数题可能会用到lambda。比如要求按字符串长度排序最常见写法是sorted(words, keylambda x: len(x))简洁明了。但我想提醒一点如果这个逻辑比较复杂或者你需要复用就别硬抽象成lambda。有一位同学跟我讨论题目时非要把一个四行的逻辑塞进lambda里写得极其难读调试还麻烦。OJ只判结果不看代码风格华丽程度但你自己后续复习时要能看懂。我的建议是能写普通函数说明白就用普通函数只有当逻辑简单到一句话能说清才用lambda。这不影响AC但能让你的解题过程更顺畅。4. 实操过程整理这套AC解答时我的完整做法4.1 题目分类与归档规则整理解答汇总不只是把AC代码堆在一起那样过一个月你自己都懒得翻。我自己的做法是按章节主题建目录再按题型拆成子目录。第六章的函数题我会分成“基础函数”“递归专项”“列表/字符串处理”三个文件夹每道题命名带上题目的功能关键词比如“6-3-字符串反转函数”“6-9-斐波那契递归改迭代”。这样做的原因是复习时可以按题型集中刷而不是按题号从头翻到尾。PTA的题目顺序本身不一定按难度排但题型分类能让你集中攻克同一个薄弱点。4.2 注释写什么才有价值很多人的代码注释要么是零要么全写废话像# 定义函数这种注释等于没写。我整理AC答案时给自己定了几条规则注释要说明意图不描述动作关键边界条件必须写清楚一个地方如果容易踩坑才值得写注释。比如def duplicate_removal(lst): # 题目要求返回新列表不能修改原列表因此先切片复制 result [] seen set() for item in lst: if item not in seen: seen.add(item) result.append(item) return result这段注释里最重要的一句话是“不能修改原列表”下次复习时你一眼就能想起当时为什么这么写。否则过了两个月你看到这段代码可能会疑惑为什么不直接在原列表上去重。4.3 AC一次不等于真的会了整理AC解答有个心理陷阱提交通过那一刻人就放松了觉得这题已经掌握。我自己后来刷其他题时发现隔一周再写同一题经常写不顺。后来养成一个习惯每道题AC之后先记录代码然后合上所有参考答案等几天后凭记忆重写一遍能不看任何提示写出来并通过才算真正掌握。这个习惯放到第六章尤其有效因为这一章题量不小题目之间的解法又有相似性很容易混。比如今天写了斐波那契的矩阵快速幂版本明天写爬楼梯其实底层是同一套思路能默写出来才说明你理解的是通法而不是背下的具体代码。5. 刷题现场常见报错与排查实录5.1 非零返回先看报错类型再动手第六章最容易把人吓住的报错就是“非零返回”。这个词看起来像系统错误其实翻译过来就是程序运行时崩了。常见原因是递归没有终止条件导致栈溢出或者访问了不存在的下标。遇到非零返回我建议把提交页面的错误信息展开看。如果提示 RecursionError基本可以确定是递归深度超限优先检查边界条件如果是 IndexError就检查列表下标是否越界尤其是空列表和只有一个元素的边界输入。排查时可以在本地构造这类边界输入多打几个print判断程序执行到哪一步定位速度会快很多。有一种情况容易忽略就是我前面提到的函数内部副作用函数里修改了传入列表后续代码再访问这个列表时取到了被改过的值运行过程不报错但输出全乱。这种最难排查所以写函数时开头先判断一下这份数据能不能改。5.2 隐藏测试点空值与边界值是重灾区PTA的每个测试点都有设计意图其中必有一两个藏在边界值上。第六章的函数题常见的隐藏测试包括空字符串、长度为1的列表、n等于0或1、负整数、非数字字符。举个例子实现一个统计字符串长度的函数你写成for i in range(len(s))思路没问题但如果某个测试点传入了空字符串你的循环体一旦无条件访问s[0]就会直接越界。所以我写这类函数时习惯在开头就把边界情况单独处理掉宁可多写两行if也不赌测试点里没有空值。这类细节不写进教材但判题系统不会放过你。5.3 浮点数比较引发的不稳定第六章如果涉及除法或者开方比如判断某数是不是完全平方数AC率会明显低一截因为浮点数比较有精度问题。最常见的是int(sqrt(n)) sqrt(n)在n很大时浮点数开方取整后再平方可能不等于原数。稳妥做法是尽量避免浮点比较用整数运算判断比如计算整数平方根后检查平方是否等于原数。这种处理在第六章出现的频率不高但一旦出现很多人的本地样例都能过隐藏大数测试点就会挂。我整理答案时看到这种解法会在注释里特别标注“浮点数比较不可靠”提醒自己以后看到类似题优先想整数做法。6. 给正在刷这一章的人几点建议6.1 别急着看别人的AC代码在OJ上卡题时人很容易想快速找一份AC代码抄上去。这个做法最大的问题是第六章函数题的核心考点是代码的组织方式和函数行为抄答案只能过一道题下一道同类型题目换个外壳你可能还是不会。我整理这份AC解答汇总本质上是为了给后来者提供一个参考的终点而不是起点。卡题半小时之后看答案没问题但看答案之前至少要自己写过一版哪怕这版是错的你的大脑才会对解法中的关键点有记忆否则看答案就是看天书关掉页面全忘光。6.2 本地测样例时加一份“手写测试集”PTA的公开样例通常只有一两组提交前可以在本地把边界输入全部试一遍。比如函数接收的是正整数至少测一遍1接收的是字符串至少测一遍空字符串接收的是列表至少测一遍空列表和单元素列表。此外还可以模拟主程序的调用方式如果判题程序调用多次你的函数是否能重复执行且结果一致。这套手写测试不需要很复杂就是题目给的样例之外额外想几个极端值。实际操作时会发现很多隐藏测试点就是在这些极端值上报错的提前测出来能省不少提交次数。6.3 把第六章当成前后知识的分水岭第六章的内容衔接了前面的基础语法也引出了后面更复杂的程序设计思想。如果这章的函数题刷得扎实后面讲到模块、类、文件操作时你会觉得很多坑似曾相识。举个例子理解了可变对象的引用传递后面接触类中修改对象属性就会更容易理解了作用域后面调试闭包也会少走弯路。反过来说这一章如果只是背了几道题的答案混过去后面的题目会反复绊你。我整理完全部AC解答后最深的体会是OJ不会因为你理解原理就给分它只看代码的行为是否正确但只有理解了原理你写的代码才能在各种测试点下行为都正确。刷完第六章以后再看前面的代码会有一种“我当时怎么写得那么乱”的感觉这就是进步的信号。我在实际整理这套解答时还发现一个有意思的现象同一道题不同人有不同的AC写法有的简短但难以理解有的冗长但每一步都很清晰。对于学习和复习来说清晰比聪明更重要。PTA的判题不会为“巧妙”加分但你的未来复习会为“易读”省下大量时间。希望这份关于第六章的拆解能帮你少走几个来回也希望你自己动手整理一份只属于你的AC解答汇总那比看我这份有用得多。