1. 从题库到能力:为什么第三章值得反复刷
Python 基础语法里,第三章通常是从“会写一行代码”跨到“能独立完成一个小逻辑”的分水岭。很多初学者在第二章还能靠记忆应付过去,到了第三章就开始卡壳——循环嵌套写不对、列表索引越界、字符串切片搞混、输出格式差一个空格就全错。这不是智力问题,而是编程思维还没有从“翻译题目”切换到“拆解问题”。
PTA 平台的 Python 编程题第三章(3-1 到 3-22)恰好覆盖了这几个最容易翻车的知识点:分支结构、循环控制、字符串处理、列表基础操作、简单算法入门。题目本身不难,但每道题都在逼你养成一个习惯——先想清楚输入输出长什么样,再动手写逻辑。这个习惯一旦养成,后面第四章的函数、第五章的文件操作都会顺很多。
这篇文章适合两类人:一类是正在上 Python 课程、被 PTA 作业卡住的学生,另一类是自学 Python 但发现自己“看得懂代码、写不出程序”的初学者。我会把 3-1 到 3-22 这 22 道题按知识点分组拆解,每道题给出思路分析、参考代码、常见错误和避坑技巧。代码全部基于 Python 3.x,可以直接在 PTA 上提交验证。
提示:PTA 的判题系统对输出格式极其敏感,多一个空格、少一个换行都会判错。建议每道题先在本地用
print(repr(输出))检查一遍再提交。
2. 第三章知识地图与题目分组
2.1 这 22 道题到底在考什么
把 3-1 到 3-22 的题目快速过一遍,会发现它们并不是随机排列的,而是按照知识点递进设计的。大致可以分成四个梯队:
| 梯队 | 题号范围 | 核心考点 | 难度感受 |
|---|---|---|---|
| 基础分支 | 3-1 ~ 3-5 | if-elif-else、条件表达式、闰年判断 | 入门 |
| 循环入门 | 3-6 ~ 3-11 | for/while、range、累加累乘、数列求和 | 简单 |
| 字符串与列表 | 3-12 ~ 3-17 | 切片、查找、替换、列表增删改查 | 中等 |
| 综合应用 | 3-18 ~ 3-22 | 嵌套循环、简单算法、格式化输出 | 偏难 |
这个分组不是官方给的,是我自己刷完之后按“卡壳频率”重新整理的。你会发现,卡壳最多的不是难题,而是那些看起来简单但输出格式刁钻的题。比如 3-5 的闰年判断,逻辑三行就写完了,但很多人因为没处理“整百年份必须能被 400 整除”这个边界条件而反复提交失败。
2.2 刷题前必须搞清楚的三个概念
在进入具体题解之前,有三个概念必须先说清楚,否则后面每道题都会踩坑。
第一个是range()的边界。range(1, 5)生成的是 1、2、3、4,不包含 5。这个“左闭右开”的规则在循环里反复出现,比如“输出 1 到 n 的所有整数”要写range(1, n+1),少写一个+1就会漏掉最后一个数。我见过太多人在 3-8 这种累加题上因为这个问题卡了半小时。
第二个是字符串的不可变性。Python 里字符串一旦创建就不能修改,所有“修改”操作实际上都是返回一个新字符串。比如s.replace('a', 'b')不会改变s本身,必须用s = s.replace('a', 'b')才能生效。3-14 和 3-15 这两道字符串题,很多人就是在这里翻车的。
第三个是列表索引从 0 开始。第 1 个元素是lst[0],第 n 个元素是lst[n-1]。负索引lst[-1]表示最后一个元素,这个特性在处理“取末尾字符”时很好用,但初学者容易搞混正负索引的边界。
注意:PTA 上有些题目会给出“输入保证合法”的说明,但这不代表你可以忽略边界情况。比如输入可能为空、可能只有一行、可能末尾有空格,这些都要在本地测试时考虑到。
3. 分支结构题组(3-1 ~ 3-5)逐题拆解
3.1 3-1 到 3-3:条件判断的基本功
这三道题是分支结构的入门,核心就一件事——把题目里的自然语言条件翻译成 Python 的布尔表达式。
3-1 通常是“判断一个数是正数、负数还是零”。看起来简单,但要注意题目要求的输出格式。有的题目要求输出 “positive”、“negative”、“zero”,有的要求输出中文,还有的要求输出 “+” “-” “0”。一定要逐字对照题目给的输出样例,不要凭感觉写。
参考代码框架:
n = int(input()) if n > 0: print("positive") elif n < 0: print("negative") else: print("zero")3-2 一般是“判断奇偶数”或者“判断能否被某数整除”。这里有个小技巧:判断偶数可以用n % 2 == 0,也可以用n & 1 == 0(位运算),但后者可读性差,不建议在初学阶段用。代码是写给人看的,其次才是给机器执行的。
3-3 开始引入多条件判断,比如“根据成绩输出等级”。这种题的关键是条件顺序。如果你先判断score >= 60输出“及格”,再判断score >= 90输出“优秀”,那 95 分也会先命中“及格”。正确的做法是从高到低判断,或者用elif确保互斥。
score = int(input()) if score >= 90: print("优秀") elif score >= 80: print("良好") elif score >= 60: print("及格") else: print("不及格")实操心得:写多分支判断时,先在纸上画出数轴,标出每个分支覆盖的区间,确认没有重叠和遗漏再写代码。这个习惯能帮你省下大量调试时间。
3.2 3-4 到 3-5:闰年判断与边界条件
3-4 和 3-5 通常涉及闰年判断或者类似的“多条件组合”问题。闰年的规则是:能被 4 整除但不能被 100 整除,或者能被 400 整除。翻译成 Python:
year = int(input()) if (year % 4 == 0 and year % 100 != 0) or (year % 400 == 0): print("yes") else: print("no")这道题的坑在于运算符优先级。and的优先级高于or,所以a and b or c等价于(a and b) or c。如果你写成year % 4 == 0 and year % 100 != 0 or year % 400 == 0,逻辑是对的,但可读性差。加括号不丢人,丢人的是逻辑写错了还找不到原因。
另一个常见错误是忘记处理“整百年份”。比如 1900 年能被 4 整除,也能被 100 整除,但不能被 400 整除,所以不是闰年。很多人只写了year % 4 == 0就提交,结果在 1900 这个测试点上挂了。
注意:PTA 的测试数据通常包含边界值,比如 2000 年(闰年)、1900 年(平年)、2024 年(闰年)。本地测试时至少要把这三个年份都跑一遍。
4. 循环结构题组(3-6 ~ 3-11)核心套路
4.1 3-6 到 3-8:累加、累乘与数列求和
这三道题是循环的入门,核心套路就一个——初始化累加器,然后在循环里更新它。
3-6 通常是“求 1 到 n 的和”。代码很简单:
n = int(input()) total = 0 for i in range(1, n + 1): total += i print(total)但这里有个隐藏考点:n 可能很大。如果 n 是 10^9,用循环累加会超时。不过 PTA 第三章的题目通常不会卡这个,n 一般在 100 以内。但你要有这个意识——循环的时间复杂度是 O(n),能用公式的地方尽量用公式。1 到 n 的和可以用n * (n + 1) // 2直接算,O(1) 搞定。
3-7 一般是“求 n 的阶乘”。这里要注意total的初始值必须是 1,不是 0。我见过有人写total = 0然后total *= i,结果永远是 0。累加初始化为 0,累乘初始化为 1,这是铁律。
3-8 开始引入数列求和,比如“求 1 + 1/2 + 1/3 + ... + 1/n”。这里要注意浮点数精度。Python 的float是双精度,一般够用,但如果题目要求保留多位小数,要用格式化输出:
n = int(input()) total = 0.0 for i in range(1, n + 1): total += 1 / i print(f"{total:.6f}")实操心得:PTA 上浮点数输出通常要求保留 6 位小数,用 f-string 的
:.6f最方便。不要用round(),因为round()返回的是数值,输出时可能不会补零。
4.2 3-9 到 3-11:循环控制与嵌套入门
3-9 和 3-10 通常涉及break和continue的使用。比如“找到第一个满足条件的数”用break,“跳过某些数”用continue。这里的关键是理解循环的执行流程,建议在纸上画流程图,标出break和continue跳转的位置。
3-11 开始引入简单的嵌套循环,比如“输出乘法表”或者“打印三角形”。嵌套循环的难点在于内外层变量的关系。以打印直角三角形为例:
n = int(input()) for i in range(1, n + 1): for j in range(i): print("*", end="") print()外层i控制行数,内层j控制每行的星号数。print()在內层循环结束后执行,负责换行。很多人忘记写最后的print(),导致所有输出挤在一行。
注意:
print()默认换行,print("*", end="")表示不换行。在嵌套循环里,内层用end="",外层用默认换行,这个模式要记牢。
5. 字符串与列表题组(3-12 ~ 3-17)实战技巧
5.1 3-12 到 3-14:字符串切片与查找
字符串是 Python 里最常用的数据类型之一,第三章的字符串题主要考三个操作:切片、查找、替换。
3-12 通常是“字符串反转”或者“取子串”。Python 里字符串反转用切片最方便:s[::-1]。这个语法的含义是“从头到尾,步长为 -1”,也就是倒序。取子串用s[start:end],注意end不包含在内。
3-13 一般是“查找字符位置”。可以用s.find(ch),返回第一次出现的索引,找不到返回 -1。也可以用s.index(ch),找不到会抛异常。在 PTA 上建议用find(),因为不需要处理异常。
3-14 开始涉及替换和统计。比如“把字符串里的所有空格替换成逗号”:
s = input() s = s.replace(" ", ",") print(s)这里再次强调:replace()返回新字符串,必须重新赋值。如果你写s.replace(" ", ",")然后直接print(s),输出的还是原字符串。
实操心得:处理字符串时,先用
print(repr(s))看一下字符串的真实内容,确认有没有隐藏的空格或换行符。PTA 的输入有时会带末尾空格,直接比较会出错。
5.2 3-15 到 3-17:列表基础操作
列表题的核心是增删改查四个操作:
| 操作 | 方法 | 说明 |
|---|---|---|
| 增 | append(x) | 末尾添加元素 |
| 删 | remove(x) | 删除第一个值为 x 的元素 |
| 改 | lst[i] = x | 修改指定索引的元素 |
| 查 | x in lst | 判断元素是否存在 |
3-15 通常是“列表去重”或者“列表排序”。去重可以用set(),但set是无序的,如果题目要求保持原顺序,需要手动遍历:
lst = list(map(int, input().split())) result = [] for x in lst: if x not in result: result.append(x) print(result)3-16 和 3-17 开始涉及列表的遍历和条件筛选。比如“输出所有大于平均值的数”。这种题分两步:先求平均值,再遍历筛选。
lst = list(map(int, input().split())) avg = sum(lst) / len(lst) for x in lst: if x > avg: print(x, end=" ")注意:
input().split()默认按空格分割,返回字符串列表。如果题目输入的是整数,要用map(int, ...)转换。如果输入在一行里用逗号分隔,要用split(",")。
6. 综合应用题组(3-18 ~ 3-22)思路拆解
6.1 3-18 到 3-20:嵌套循环与简单算法
这三道题开始把前面的知识点组合起来,比如“打印菱形”、“求素数”、“进制转换”。
以“打印菱形”为例,思路是把它拆成上半部分和下半部分。上半部分是一个正三角形,下半部分是一个倒三角形。关键是找到每行的空格数和星号数与行号的关系:
n = int(input()) for i in range(1, n + 1): print(" " * (n - i) + "*" * (2 * i - 1)) for i in range(n - 1, 0, -1): print(" " * (n - i) + "*" * (2 * i - 1))“求素数”的经典写法是试除法,从 2 试到sqrt(n):
n = int(input()) is_prime = True if n < 2: is_prime = False else: for i in range(2, int(n ** 0.5) + 1): if n % i == 0: is_prime = False break print("yes" if is_prime else "no")实操心得:判断素数时,循环上界用
int(n ** 0.5) + 1比n快很多。虽然第三章的数据规模不大,但养成这个习惯对后面做算法题有帮助。
6.2 3-21 到 3-22:格式化输出与综合应用
最后两道题通常是综合应用,涉及格式化输出和多个知识点的组合。比如“输出成绩表”或者“统计字符类型”。
格式化输出推荐用 f-string:
name = "张三" score = 95.5 print(f"{name}: {score:.1f}")统计字符类型是经典题:统计一串字符里字母、数字、空格、其他字符的个数。
s = input() letters = digits = spaces = others = 0 for ch in s: if ch.isalpha(): letters += 1 elif ch.isdigit(): digits += 1 elif ch.isspace(): spaces += 1 else: others += 1 print(letters, digits, spaces, others)这道题的坑在于输入可能包含多行。如果题目说“输入一行字符串”,用input()就够了;如果说“输入多行”,需要用sys.stdin.read()或者循环读取。
注意:
isalpha()判断的是 Unicode 字母,中文也会返回 True。如果题目只要求统计英文字母,需要用ch.isascii() and ch.isalpha()。
7. 常见问题与排查技巧实录
7.1 PTA 判题常见错误速查表
| 错误提示 | 可能原因 | 解决方法 |
|---|---|---|
| 答案错误 | 输出格式不对 | 用repr()检查输出,对照样例逐字比较 |
| 运行超时 | 循环次数过多 | 检查是否有死循环,考虑用公式替代循环 |
| 非零返回 | 程序抛异常 | 检查输入转换、索引越界、除零 |
| 段错误 | 递归过深或内存问题 | 第三章一般不涉及,检查是否有无限递归 |
| 格式错误 | 多余空格或换行 | 检查print的end参数和末尾换行 |
7.2 我踩过的三个坑
第一个坑:input()读取多行。有些题目说“输入若干行,每行一个整数”,但没给行数。这时候不能只调一次input(),要用sys.stdin或者try-except:
import sys for line in sys.stdin: n = int(line.strip()) # 处理 n第二个坑:浮点数比较。PTA 有些题要求“判断两个浮点数是否相等”,直接用==会因为精度问题出错。正确做法是判断差值是否小于一个极小值:
if abs(a - b) < 1e-6: print("equal")第三个坑:列表越界。在循环里访问lst[i+1]时,如果i到了最后一个索引,就会越界。解决办法是循环范围改成range(len(lst) - 1),或者用try-except捕获。
实操心得:每道题提交前,至少用三组数据测试——最小输入、最大输入、边界输入。比如 n=0、n=1、n=100。这三组能过,基本就没问题了。
7.3 调试技巧:打印中间变量
PTA 上看不到运行过程,所以本地调试很重要。在关键位置加print输出中间变量,确认逻辑是否正确:
n = int(input()) total = 0 for i in range(1, n + 1): total += i print(f"i={i}, total={total}") # 调试用,提交前删掉 print(total)提交前记得把调试语句删掉,否则会输出多余内容导致答案错误。
8. 从第三章到后续章节的衔接建议
第三章刷完之后,你会对 Python 的基本控制流有一个完整的认识。接下来第四章通常是函数,第五章是文件操作,第六章是面向对象。这三章的共同特点是代码量开始变大,所以第三章养成的习惯会直接影响后面的学习效率。
我的建议是:第三章的 22 道题至少刷两遍。第一遍按题号顺序做,遇到不会的看题解;第二遍打乱顺序做,只看题目不看题解,逼自己独立完成。第二遍如果能在 40 分钟内做完 22 道题,说明第三章的知识点已经内化了。
另外,PTA 上的题目只是手段,不是目的。真正重要的是把每道题的思路用自己的话讲一遍。如果你能对着空气把“闰年判断”的逻辑讲清楚,把“素数判断”的循环边界说明白,那你就真的掌握了。这比刷一百道题都有用。
最后分享一个小技巧:建一个自己的“错题本”,把每次提交失败的原因记下来。比如“3-5 忘记处理 1900”、“3-14 忘记重新赋值字符串”。考前翻一遍,比重新刷题效率高得多。