
“Python装好了基础语法也过了一遍然后呢”这是我被问到最多的问题。绝大多数人的下一步是跑去网上看教程今天看列表理解、明天看字典合并看了半个月真到自己写代码时却还是磕磕绊绊。我个人的建议一直很朴素看完一个知识点立刻用编程题去锤它。这套100 Python编程题附答案就是按这个逻辑整理出来的。它不是网上那种随手一搜就扔给你的“50道练习题”清单而是一条从变量、条件、循环、字符串、列表、字典到函数、递归、排序、动态规划再到文件处理和词频统计的完整训练链路。适合两类人一类是刚学完Python基础、想用刷题把知识焊在脑子里的新手另一类是准备笔试面试、需要用题目快速找回代码手感的人。今天我把整套题单的设计思路、刷题方法以及其中最值得反复咀嚼的一批题目和答案单独拎出来细讲顺便把新手做题时最容易踩的坑一次性说清楚。1. 这套题单是怎么设计的先说结论一份好的Python编程题单绝不是“越多越好”而是“越有梯度越好”。市面上很多题库之所以刷不下去就是因为题目之间没有递进关系二十道题翻来覆去都在考同一个知识点。我在整理这套题的时候刻意把全部题目划分成五个阶段每个阶段对应一个学习层次前一个阶段是后一个阶段的铺垫后一个阶段会反过来加深对前一个阶段的理解。1.1 五个阶段对应一条完整的学习路径这100道题我分成五个模块每个模块的题量和考察重点如下阶段覆盖知识点题量定位阶段一变量、类型转换、输入输出、条件判断、循环30道基础语法入门阶段二字符串、列表、字典、集合、元组操作25道数据结构操作阶段三函数定义、参数传递、lambda、模块导入15道代码组织能力阶段四排序、查找、递归、动态规划、经典算法20道算法思维训练阶段五文件读写、数据清洗、词频统计、小工具开发10道实战落地为什么把“基础语法”和“数据结构”分开因为这两个阶段的学习目标完全不同。基础语法解决的是“这门语言能不能跑起来”的问题for循环怎么写、if判断怎么嵌套、input读进来的是字符串还是数字这些属于肌肉记忆而数据结构解决的是“用什么工具去组织数据”的问题同样是去重你会不会优先想到set或者dict.fromkeys这决定了代码的优雅程度和运行效率。先掌握语法再熟悉数据结构接着才能去谈算法这个顺序不能跳。1.2 不同基础的人怎么用这套题如果你是完全零基础我的建议是按顺序刷每天只做3到5道不要贪多。每道题做完之后把它当成一道错题来对待即使你的代码能跑出正确答案也去看看参考答案有没有更简洁的写法。很多初学者做完题就扔这是最浪费的。如果你已经有一定基础可以直接从阶段二开始。阶段一的前10道题快速扫一眼题目描述觉得自己三分钟能写出来就跳过重点把时间和精力放在后面的字符串处理和列表操作上。如果是准备秋招或者实习面试那阶段四的算法题就是重点。排序、递归、动态规划这三块是笔试的高频考点我后面会单独讲几道代表性的题目。至于阶段五的实战题面试官大概率不会直接考但它能帮你在项目经验这块有话可说。提示做题千万不要按字母顺序背题。你要做的是“按知识点刷题”而不是“按题号刷题”。同一个知识点连续做三到五道变式题比你一天做二十道不同知识点的散题更有效。2. 刷题之前先明确这三件事很多人拿到题单后的第一反应是打开编译器看着题目发呆五分钟然后偷偷翻答案。这种刷题方式一点用都没有。题目不只是用来“验证你会不会”更是用来训练你把问题拆解成代码步骤的能力。在动手之前建议先建立一套自己的做题流程。2.1 拿到题目后的标准动作我刷题这么多年总结了一个固定三步法也建议新手先照着做。第一步读题把题目的输入和输出各画一个例子。不管题目多简单都动手写一个“输入是什么、输出是什么”的对应关系。比如题目说“求两个数的和”你就写输入: 3, 5 - 输出: 8。这一步能快速帮你确认自己没有理解偏题意尤其是面试题题目读错的代价非常大。第二步用自然语言描述解题思路不要急着写代码。哪怕你写的思路是“先用for循环遍历列表再用if判断条件满足就加入新列表”这也没问题。把思路写出来之后你会发现自己对题目的结构认识清晰了很多代码不过是把这几行中文翻译成Python而已。第三步给代码加上边界情况测试。很多新手觉得“能跑就行”完全不测空列表、负数、特别大的数这些边界输入。实际上判断一个程序员水平最直接的方式就是看他会不会主动测边界。比如判断素数的题目如果你没测过 n1 和 n2很可能代码就是错的。2.2 答案什么时候看我的建议是一道题如果15分钟还没有任何思路可以看答案但看完答案之后必须做三件事。第一把答案关掉靠记忆和理解重新默写一遍。如果你在默写的过程中卡住了说明你只是眼睛看懂了手还没懂。第二给这份答案挑毛病试着优化它。参考答案只是“一种可行解”不是“唯一解”。比如题目要求反转字符串参考答案用了切片你能不能写出循环版本能不能写出递归版本如果能这道题才算吃透。第三第二天再把这道题重做一遍。这是对抗遗忘最朴实也最有效的方法。我见过太多人刷题时一片火热第二天连题目问什么都想不起来这就是典型的“假刷题”。2.3 做完一道题一定要做“变式”题目本身是死的但你可以把它变活。每做完一道题试着改一两个条件变成一个新题目然后继续做。举个例子题目要求“求一个数组中所有元素的最大值”做完之后你可以把它改成“求第二大值”再改成“求前k个最大值”再改成“如果数组是循环数组怎么求最大值”。每改一次你对这个知识点的理解就深一层。这套题单里的很多题其实都是这么从经典题演变出来的。所谓100道题并不是真有100个完全独立的题目而是用核心题目派生出来的变式这样练出来的代码能力才真正可变现。3. 精选题目与完整解答附答案完整题单有100多道全放出来文章就太长了。这里我按阶段挑了10道最有代表性的题把题目、思路、参考代码和易错点都讲清楚。这10道题覆盖了基础的语法、常用的数据结构、递归算法、动态规划以及实战中的文件处理你把这10道吃透再回去刷完整版就会轻松很多。3.1 基础语法题从这两道先热身题目1判断一个数是否为素数。def is_prime(n): if n 2: return False i 2 while i * i n: if n % i 0: return False i 1 return True这道题是笔试里的常客。易错点有三个一是忘记处理 n2 的情况1既不是素数也不是合数二是循环范围写成 range(2, n)当 n 很大时会超时只需要循环到根号 n 就够三是没有单独考虑 n2其实 n2 时循环条件不成立直接返回 True是正确的不需要额外判断。题目2输出九九乘法表下三角。for i in range(1, 10): for j in range(1, i 1): print(f{j}*{i}{i * j:2}, end ) print()输出效果是经典的下三角格式。这个题表面上考双层循环实际考的是内层循环的范围控制range(1, i1)决定了每行只输出到当前行号。很多初学者会写成range(1, 10)结果每行都输出九列。另外:2是格式化的小技巧让乘积右对齐占两位输出会整齐很多这个细节在真实项目里打印表格时非常实用。3.2 字符串和列表日常写代码的高频场景题目3反转字符串。s hello # 方法一切片 r1 s[::-1] # 方法二内置函数 r2 .join(reversed(s)) # 方法三纯循环 r3 for ch in s: r3 ch r3这道题考察的是“一个需求多种实现”。面试时如果只写出第一种我可以理解你熟悉切片如果能写出第二种说明你对内置函数有了解能写出第三种说明你清楚字符串反转的底层逻辑是“把字符依次拼到新串的前面”。实际工程里首选第一种一行代码搞定效率也高。但刷题时建议把三种都写一遍因为后两种能让你真正理解字符和字符串之间的关系。题目4列表去重且保持原顺序。data [3, 1, 2, 1, 3, 4, 2] # 方法一遍历 result [] for x in data: if x not in result: result.append(x) # 方法二利用字典键的唯一性Python 3.7 字典保序 result2 list(dict.fromkeys(data))这个题目极其经典。用集合set(data)去重虽然简单但结果顺序是不确定的一旦题目要求“保持原顺序”集合就满足不了。方法一是最稳妥的解法时间复杂度是 O(n^2)数据量小的时候无所谓方法二用dict.fromkeys做去重既能保证唯一性又能保持插入顺序而且时间复杂度是 O(n)。这里有个考点Python 3.6 之前字典不保证顺序3.7 之后才正式成为语言规范所以方法二在面试时最好说明一下版本前提。3.3 函数与递归从会写到会想题目5斐波那契数列第 n 项。# 解法一递归不推荐重复计算太多 def fib1(n): if n 1: return n return fib1(n - 1) fib1(n - 2) # 解法二迭代推荐 def fib2(n): a, b 0, 1 for _ in range(n): a, b b, a b return a这道题是递归入门的必刷题。递归版本的代码确实最贴近数学定义但最大的问题是重复计算计算 fib(10) 时fib(3) 的中间结果会被重复算十几次。数值一大程序就像卡死一样。迭代版本用两个变量滚动更新每次循环只保留前两项时间复杂度 O(n)空间复杂度 O(1)是工程中最推荐的写法。做这道题时你可以顺手用 print 统计递归被调用了多少次亲眼看一看指数爆炸的威力。题目6爬楼梯一次可以上1级或2级求上到第 n 级共有多少种走法。def climb_stairs(n): if n 2: return n a, b 1, 2 for _ in range(3, n 1): a, b b, a b return b这就是斐波那契数列的换皮版。第 n 级的走法等于第 n-1 级的走法加上第 n-2 级的走法因为最后一步要么是从 n-1 级跨一级上来要么是从 n-2 级跨两级上来。很多人能写出fib却认不出爬楼梯也是同一个问题刷题时一定要训练这种“识别模型”的能力。这道题答得好不好往往能反映一个人有没有真正理解动态规划里的状态转移思想。3.4 算法进阶排序和动态规划题目7手写冒泡排序并做优化。def bubble_sort(arr): n len(arr) for i in range(n - 1): swapped False for j in range(n - 1 - i): if arr[j] arr[j 1]: arr[j], arr[j 1] arr[j 1], arr[j] swapped True if not swapped: break return arr冒泡排序本身不难难在优化上。内层循环每轮都会确定末尾一个元素的最终位置所以内层范围是n - 1 - i。优化点是引入swapped标志如果某一轮从头到尾都没有发生交换说明整个数组已经有序直接退出。这个优化在数据基本有序时能把时间复杂度从 O(n^2) 降到 O(n)。笔试时写出基础版能证明你了解排序流程写出优化版才能证明你思考过算法性能。题目8李白打酒。李白提着酒壶上街遇到酒店就把壶中酒加一倍碰到花就喝掉一斗。先后遇到3次酒店、3次花最后一次遇到花后壶中酒正好喝光。问原来壶中有多少酒def solve(): wine 0 # 逆推最后一次遇花后为0遇花前为1遇店前为遇店后的一半 for _ in range(3): wine 1 # 逆推遇花喝掉的1斗加回来 wine / 2 # 逆推遇店加一倍变成原量的一半 return wine print(solve()) # 0.875答案是0.875斗也就是7/8斗。这道题考的是逆向思维不适合正向设方程硬解更适合从结果往前推。最后一次见花喝光说明见花前壶中有1斗倒数第二次遇店加一倍说明遇店前只有0.5斗再往前是遇花、遇店这样一步一步倒着还原。这种“从最终状态逆推初始状态”的思路在动态规划、递归回溯类题目里非常常见。题目90/1背包问题。给定背包容量 capacity以及每个物品的重量和价值求能装入背包的最大价值。def knapsack(weights, values, capacity): n len(weights) dp [0] * (capacity 1) for i in range(n): for w in range(capacity, weights[i] - 1, -1): dp[w] max(dp[w], dp[w - weights[i]] values[i]) return dp[capacity]这是面试和笔试中的高频难题也是动态规划的入门经典。核心思路是用一维数组dp[w]表示容量为w时能装下的最大价值每遍历一个物品就倒序更新所有容量。为什么要倒序因为正序遍历时小的容量有可能在这一轮已经被当前物品更新过了如果再拿去更新大容量就会导致同一个物品被重复放入也就变成了“完全背包”。倒序遍历保证每个状态只依赖上一轮的结果也就是每个物品只用一次。举个例子weights[1,3,4]values[15,20,30]capacity4。最优解是选第1个物品和第3个物品总重量5不对第1个重量1价值15第3个重量4价值30加起来重量5超过容量4。实际上是选第2个物品第1个物品总重量4总价值35。所以最大价值是35。这个例子跑一遍代码你就能直观感受到状态转移在怎么发生。3.5 实战风格把题做成小工具题目10统计一篇文章中出现次数最多的10个单词。import re from collections import Counter def top_n_words(text, n10): words re.findall(r[a-z], text.lower()) return Counter(words).most_common(n) text Python is great. Python is popular, and Python is easy to learn. But learning Python takes practice, practice, and more practice. print(top_n_words(text))这道题虽然叫“编程题”但它更像一个小型实战任务。它综合考察了字符串转小写、正则表达式分词、Counter计数、列表排序等多个知识点。真实项目里处理文本数据时99%都会遇到这种情况。注意re.findall(r[a-z], text.lower())这一步它把大写转成小写并按字母序列提取单词同时把标点符号过滤掉。如果你对正则不熟这道题就是一个很好的查漏补缺入口。4. 做题时最容易踩的坑与调试技巧刷题刷多了你就会发现很多题不是不会做而是被各种“低级错误”卡住。这里我把新手做题时最常遇到的几类报错整理成一个速查表你以后看到报错信息可以先对照一下。报错信息出错原因解决方案IndentationError: expected an indented block代码块缩进错误统一用4个空格缩进不要混用Tab和空格IndexError: list index out of range列表下标越界检查循环边界确认索引是否超过len(list)-1KeyError: xxx字典中不存在该键用dict.get(key)代替dict[key]TypeError: int object is not iterable对整数使用for循环确认是否少写了一个range()NameError: name x is not defined变量名拼写错误或未定义检查变量名拼写与作用域UnboundLocalError: local variable x referenced before assignment函数内使用全局变量前未声明在函数内用global x声明后再使用除了这些报错还有一个非常经典的坑函数内定义了一个和全局变量同名的局部变量Python 会在编译时把它判定为局部变量导致你在赋值之前就去读取它时直接报UnboundLocalError。例如count 0 def increment(): count 1 # 这里会报错原因是你对count做了赋值操作Python认为它是一个局部变量因此函数体内的count 1相当于在读取一个尚未赋值的局部变量。解决办法是在函数内开头加一句global count。这个问题很多写过一年Python的人都会踩做题时遇到了别慌知道原理就好。调试技巧方面我强烈推荐“print大法”加“二分定位法”。如果代码逻辑比较复杂比如一个函数里套了三层循环你怀疑结果算错了不要在好几处都打印而是先猜一个大概出问题的位置在可疑区域前后各加一个print看数据流到哪里就断了然后逐步缩小范围。这种做法比从头到尾一行一行读代码高效得多。还有一个非常实用的建议报错信息要看最后一行不要看最上面一行的长长堆栈。很多新手一看到红色报错就慌直接把整个报错全选复制去搜索其实 Python 已经告诉你了错误类型和原因就在最后一行NameError、TypeError这些关键字就是答案。先读懂最后一行再去搜原因效率完全不一样。5. 刷完100道题之后怎么走刷完100多道题不代表学习的终点。恰恰相反它只是让你有了“能读代码、能写代码”的基本盘。接下来要做的事情是把题目里的知识点带到真实场景里。5.1 对题目做二次加工第一遍刷题强调的是“会做”第二遍刷题就应该追求“做精”。同一道冒泡排序你可以给自己加两个新要求一是把函数改成接收任意可迭代对象比如元组、集合返回排序后的列表二是给函数加上耗时统计分别测试一万个和十万个随机数据的排序时间三是用同样的输入去比较sorted()和自己手写冒泡的性能差异。这样一道题就能吃透函数的参数传递、性能分析、类型处理等多个知识点。另外强烈建议你建立一个自己的“错题仓库”。不需要复杂的工具一个GitHub仓库或者本地文件夹就行。把你做错的题、卡壳的题、觉得很妙的答案都放进去每个题目附上题干、你的原始错误代码、正确代码、错因分析。这样做半年之后再回头翻你会非常清楚自己的成长轨迹。5.2 把题目变成小项目阶段五的实战题已经接近项目形态了刷完这些题之后可以试着做几个小工具来巩固。比如一个命令行版的待办事项管理器支持添加、删除、标记完成用文件保存数据。一个文本统计分析工具读取本地txt文件输出总字数、行数、词频Top10。一个通过爬虫抓取某个公开网页标题和摘要的小脚本把结果保存成CSV。这些小工具不复杂可能只需要一两百行代码但它们的价值在于你需要自己考虑“输入是什么、输出存哪里、出错怎么办”这些都是真实开发要面对的问题。做题训练的是“局部能力”做小工具训练的才是“整体能力”。我在实际带人学Python的过程中发现刷完100道题的人和只看了教程没做题的人差的不是知识量而是“调试能力”。前者看到报错信息会先把最后一行读懂拆解问题原因后者第一反应是复制报错去搜索搜到一个答案就到处套常常越改越乱。刷题练出来的其实就是这种遇到问题不慌、能一步步分析定位的习惯。所以这套题单尽管放心去刷卡住了就看看答案看完关掉自己写写不出来就再看一遍循环几次你会发现Python那些看似零散的语法慢慢都变成了你自己的东西。