
简介这份资料是面向Python初学者的经典例题练习合集以PDF文档形式整理收录了从基础语法到简单算法的常见编程题目适合刚接触Python、需要靠动手练习来打牢基础的人群也可作为课堂教学或自学阶段的配套习题使用。压缩包内共1个PDF文件约203KB篇幅16页按题目顺序依次给出问题描述与参考实现代码涵盖循环与条件判断、函数与math模块调用、日期时间处理、字符串格式化、列表与数组思路、素数检测及水仙花数等数学算法场景还包括利润分段提成、兔子繁殖、日期天数差、分解质因数、成绩等级转换等典型题。每道题都配有可直接参考的代码片段方便读者对照理解raw_input输入、for循环遍历、while循环判断与逻辑运算的写法在模仿与改写中掌握数据类型、控制流与基本算法设计。目前已有713人学习属于轻量级、便于随时查阅的练习材料。1. 从一份 16 页的 Python 例题集说起它到底值不值得刷很多人第一次打开这类「Python 经典例题整理」的 PDF看到的是raw_input、print后面不带括号、%5d这种格式化写法第一反应是「这代码过期了跑不起来」。但把它扔进 Python 3 解释器逐条改写之后会发现这些题目真正想训练的东西和语法版本无关枚举与去重、分段计费的边界处理、完全平方数的整数判定、日期差、素数筛、递归拆解、位运算。名企笔试和 LeetCode 简单档里这些套路换个壳还在反复出现。这份 16 页的资料按程序 1 到程序 58 编号覆盖了循环嵌套、math/datetime/string模块、列表原地交换、筛法、递归、Tkinter 画布绘图。适合两类人刚学完语法但写不出完整程序的入门者用它把「会语法」变成「会解题」以及准备笔试的从业者拿它当低成本的算法热身集。麻烦在于它是 Python 2 时代的产物直接复制粘贴必然报错必须做一次系统性的迁移这篇文章就围绕这个迁移和刷题流程展开。2. Python 2 到 Python 3 的例题迁移与四道枚举类题目的重写这一章解决「拿到资料第一步做什么」。不做版本迁移后面所有题都只是纸面阅读。2.1 迁移清单raw_input、print、除法、编码声明原文代码里有四类在 Python 3 下必然失败的写法逐条对应改写规则。原写法Python 2Python 3 写法出错表现raw_input()input()NameError: name raw_input is not definedprint xprint(x)SyntaxError: Missing parentheses in call to printn / i整数除法n // i或n n // i结果变成float循环判断n ! 1行为异常print %5d%(i)print(%5d % i)语法报错# -*- coding:utf-8 -*-保留无妨Python 3 默认 UTF-8不报错但属于冗余/和//的差别在程序 14「分解质因数」里是致命的。原文用n/iPython 2 下是整除Python 3 下 90/2 得到45.0后续range(2, n1)直接抛TypeError。这是初学迁移时最容易被忽略的一处。2.2 程序 1无重复数字的三位数枚举法与去重的取舍三种三位数排列标准解法是三层循环加i ! j ! k注意!不能连写成数学形式。# 生成 1、2、3、4 组成的所有互不相同且无重复数字的三位数 count 0 results [] for i in range(1, 5): # 百位1~4 for j in range(1, 5): # 十位 for k in range(1, 5): # 个位 if i ! j and j ! k and i ! k: results.append(i * 100 j * 10 k) count 1 print(count, results)range(1, 5)上界不包含 5正好覆盖 1 到 4。条件是三个两两不等式写成i ! j ! k只在链式相等判断中有意义这里必须用and连接。答案是 24 个。常见改进用法是itertools.permutations([1,2,3,4], 3)一行出结果但面试时手写循环更能说明对枚举结构的理解两者都该会。2.3 程序 2分段奖金提成的逆序遍历结构这题的价值不在算钱在于「从最高档位往下累加」的写法。原代码用两个列表arr和rat配合逆序索引是个很好的模板。# 分段累进提成从最高档位开始逐段计算 profit int(input(Enter the profit:)) thresholds [1000000, 600000, 400000, 200000, 100000, 0] # 档位边界 rates [0.01, 0.015, 0.03, 0.05, 0.075, 0.1] # 各段对应费率 bonus 0 for idx in range(6): if profit thresholds[idx]: bonus (profit - thresholds[idx]) * rates[idx] print((profit - thresholds[idx]) * rates[idx]) profit thresholds[idx] # 把利润压回当前档位下一轮只算剩余部分 print(bonus)关键在profit thresholds[idx]这一句第一次循环处理的是超过 100 万的部分算完后把profit降到 100 万下一轮比较的就是「10 万到 100 万」这一段。两个列表必须一一对齐thresholds[idx]是区间下界rates[idx]是该区间费率。如果输入利润为负这段代码不会报错但结果为零实际项目里应该在入口加if profit 0: raise ValueError。2.4 程序 3 与程序 4完全平方数判定和日期差程序 3 的原始写法用math.sqrt(num100)-int(math.sqrt(num100)) 0判断是否完全平方浮点误差在数值不大时可用但更稳的做法是取整后回代验证。import math num 1 while True: r1 int(math.isqrt(num 100)) # Python 3.8 提供整数平方根 r2 int(math.isqrt(num 268)) if r1 * r1 num 100 and r2 * r2 num 268: print(num) break num 1math.isqrt返回整数平方根避免sqrt的浮点误差再用r*r 目标值回代判定就是精确的。程序 4 用datetime.strptime解析%Y%m%d格式把当年 1 月 1 日也解析成datetime两者相减取.days再加 1。注意strptime对非法日期比如 20150230会抛ValueError生产代码里要用try/except包住提示用户重新输入。3. 循环、素数筛与因子分解从暴力法到筛法的复杂度对比第二章的四题偏枚举这一章处理「关于整数的判定与分解」是笔试高频区也是性能意识开始出现的地方。3.1 程序 12试除法判素数与 sqrt 上界原代码从 2 试到int(sqrt(i))这个上界是对的若 n 有因子必有一个不超过 √n。from math import isqrt def is_prime(n): if n 2: return False for d in range(2, isqrt(n) 1): if n % d 0: return False return True primes [i for i in range(101, 201) if is_prime(i)] print(len(primes), primes)isqrt(n) 1是因为range右端开区间。101 到 200 之间共有 21 个素数。原始版本用flag变量控制循环退出可读性一般改成提前return更清晰。如果换成判 10^12 量级的单个数试除法就力不从心了那种场景要换 Miller-Rabin但这题范围完全不需要。3.2 程序 36埃氏筛法用空间换时间100 以内求素数筛法是标准答案代码比逐个试除更短。# 埃拉托斯特尼筛法标记合数剩下的即素数 n 100 is_composite [False] * (n 1) for i in range(2, isqrt(n) 1): # 只需筛到 sqrt(n) if not is_composite[i]: for j in range(i * i, n 1, i): # 从 i*i 开始标记 is_composite[j] True primes [i for i in range(2, n 1) if not is_composite[i]] print(primes)两个细节值得记外层循环到sqrt(n)即可因为更大数的倍数已经被更小因子筛过内层从i*i起步而非2*i省掉重复标记。复杂度 O(n log log n)比逐个试除的 O(n√n) 快一个量级。原文用a [0]*101加标记-1思路一致只是布尔数组语义更直白。3.3 程序 14质因数分解中的整除与除尽n int(input(Enter a number:)) print(f{n} , end ) factor 2 parts [] while factor * factor n: while n % factor 0: parts.append(factor) n // factor factor 1 if n 1: # 剩余的大质因子 parts.append(n) print( * .join(map(str, parts)))内层while保证同一个因子被除尽比如 90 会连续输出两个 2 之前的一个 2、两个 3、一个 5。外层条件用factor * factor n而不是factor n因为 n 在不断缩小循环次数大幅减少。循环结束后若n 1说明剩下的 n 本身是质数必须补进去。原始代码用n / i在 Python 3 下会得到浮点数必须改成//。3.4 程序 13 与程序 19水仙花数与完数判定的边界水仙花数的位数拆解用i % 10、(i // 10) % 10、i // 100只适用三位数。若要写通用的「n 位自幂数」改成字符串遍历或循环取位即可。完数判定要注意因子不包含自身原代码用sum n * -1预置负自身再累加因子是个取巧写法改成sum 0然后只累加i n的因子更清楚。另外完全平方数的因子成对出现isqrt上界循环里n // i和i分别累加能一次补齐一对因子避免漏掉大于 √n 的那一半。4. 递归、列表原地操作与 Tkinter 绘图的落地点前三章都是纯计算题这一章处理「会写循环但写不好递归」「会调库但不懂原地操作」「不理解 GUI 事件循环」这三类典型缺口。4.1 程序 26 到 29递归的基线与两种写法递归题最容易踩的坑是忘记终止条件或者终止条件写错。以程序 26 的阶乘为例def factorial(n): if n 1: # 基线条件0! 和 1! 都是 1 return 1 return n * factorial(n - 1) print(factorial(5)) # 120程序 27 的字符串逆序输出用递归实现思路上是「先打印最后一个字符再递归处理前 n-1 个」。程序 28 的年龄问题第五人比第一人大 4 个 2 岁写成return fun(i-1) 2基线fun(1) 10。程序 29 的位数统计和逆序打印把cnt作为参数在递归中传递这里要留意Python 没有引用传递cnt是值传递所以每层递归的cnt是独立的副本打印语句放在基线分支里才能得到正确的总位数。提示递归深度默认限制约 1000 层阶乘这类题目范围小无所谓但如果处理上万层的数据结构遍历要么改写成迭代要么用sys.setrecursionlimit调整并确认不会爆栈。4.2 程序 37 到 40列表原地交换与插入排序的手写程序 40 的数组逆序用了a[i], a[l-i-1] a[l-i-1], a[i]这是 Python 的元组打包解包右侧先构造元组再赋值不需要临时变量。手写排序程序 37时同样可以用这个技巧简化交换。# 选择排序原地交换不依赖 sort() data [5, 3, 8, 1, 9, 2, 7, 4, 6, 0] for i in range(len(data) - 1): min_idx i for j in range(i 1, len(data)): if data[j] data[min_idx]: min_idx j if min_idx ! i: data[i], data[min_idx] data[min_idx], data[i] print(data)程序 39 的「有序数组插入」是插入排序的单步版本先append扩展一个位置再从后往前把比新元素大的值右移最后把新元素放到空出的位置。要点是查找插入点和移位要合并成一次从后往前的循环否则每次插入都变成 O(n) 查找加 O(n) 移位。4.3 程序 38 与位运算题目矩阵下标的线性化3×3 矩阵用一维列表存储时主对角线元素下标是l[3*ii]即l[0]、l[4]、l[8]。副对角线是l[3*i (2-i)]。线性化的好处是输入输出简单坏处是下标公式容易写错建议先在纸上画出下标矩阵再动手。位运算那几题程序 51、54在 Python 3 下要注意数字字面量原文的a 077是 Python 2 的八进制写法Python 3 改成0o77。100 0x00F01这类取位操作和语言的版本无关但打印时用bin()看二进制形式更直观。4.4 程序 56 到 58Tkinter 画布坐标与事件循环原文from Tkinter import *在 Python 3 下要改成from tkinter import *其余 Canvas 逻辑基本通用。from tkinter import * root Tk() canvas Canvas(root, width300, height300, bggreen) canvas.pack() x0, y0, x1, y1 150, 150, 160, 160 for _ in range(19): canvas.create_rectangle(x0, y0, x1, y1) x0 - 5 y0 - 5 x1 5 y1 5 root.mainloop() # 进入事件循环窗口才会持续显示create_rectangle的参数是左上角和右下角坐标循环里每次把左上一角往左上移、右下一角往右下移就画出同心方框。mainloop()不可省略它是阻塞式的事件循环少了它就只是建好对象然后程序退出。画圆用create_oval起止坐标是外接矩形的左上和右下画直线用create_line。缩放系数j 0.3那种写法控制圆的增长速率属于美术效果不影响语法理解。5. 把 58 道题跑成可回归的测试与改写检查点零散地跑几道题容易自我感觉良好真正检验掌握程度的是把这份例题集变成一套可重复执行的测试。这一章给一个具体的落地方式。最直接的做法是每道题写一个solve_xxx()函数输入通过参数传入再用断言验证结果。以第二章的三个题为例import math def solve_permutation_three_digits(): 程序 1返回三位数个数和列表 res [i * 100 j * 10 k for i in range(1, 5) for j in range(1, 5) for k in range(1, 5) if i ! j and j ! k and i ! k] return len(res), sorted(res) def solve_bonus(profit): 程序 2分段提成 thresholds [1000000, 600000, 400000, 200000, 100000, 0] rates [0.01, 0.015, 0.03, 0.05, 0.075, 0.1] bonus 0 for idx in range(6): if profit thresholds[idx]: bonus (profit - thresholds[idx]) * rates[idx] profit thresholds[idx] return bonus def solve_prime_count(lo, hi): 程序 12区间素数 def ok(n): if n 2: return False return all(n % d for d in range(2, math.isqrt(n) 1)) return [i for i in range(lo, hi 1) if ok(i)] assert solve_permutation_three_digits()[0] 24 assert solve_bonus(1100000) 10000 4000 8000 12000 7500 10000 assert len(solve_prime_count(101, 200)) 21断言里的数字是自己按分段公式手算出来的期望值这样一旦改写引入错误就会立刻暴露。solve_permutation_three_digits用列表推导替代三层循环逻辑等价但更紧凑solve_bonus保持原地修改参数profit注意这意味着不要在多线程环境共享输入变量。几个改写时的检查点除法全部检查搜索代码里所有的/确认是否需要改成//。整数题里用错一次类型就从 int 变成 float。输入用input而非raw_input并且对int()转换加try/except ValueError因为用户输入字母时原代码直接崩。打印格式统一到 f-string老的%5d、%d *在 Python 3.6 都能写得更清楚比如print(f{i} *, end)。随机数验证排错对于枚举类题目用小规模输入比如把程序 1 的 range 改成 1~3手动推演预期结果再和代码输出对比比盯着代码找 bug 快得多。如果想更进一步可以用unittest或pytest把这些断言组织成test_examples.py每道题一个测试方法。跑一遍全绿说明这 58 道题的核心逻辑你都真正吃透了而不是抄了一遍能跑就完事。这套测试文件本身也能当作后续复习时的速查清单哪道题忘了回来看对应的solve_xxx和断言比翻 16 页 PDF 快。本文还有配套的精品资源点击获取