IO篇
很多Python选手在平时练习时习惯用 input() 和 print(),到了赛场上却发现:
原因很简单:Python的输入输出(IO)速度远慢于C/C++,且格式要求极其严格。如果你忽略了IO优化,即使算法正确也可能被卡掉。
为什么输入处理很重要?
- 速度差异:
sys.stdin.readline() 比 input() 快约5-10倍;sys.stdin.read() 一次性读入全部再解析,对于大数据最快。 - 格式陷阱:输入可能包含多余空格、换行,甚至空行,需要稳健处理。
- 多组测试数据:蓝桥杯有的题目是多组输入,直到EOF结束。
三种主要输入方式
1. input() —— 简单场景
n = int(input()) # 读取整数
a, b = map(int, input().split()) # 读取一行两个整数
data = list(map(int, input().split())) # 读取一行多个整数
2. sys.stdin.readline() —— 推荐日常使用
- 注意:返回的字符串包含末尾换行符\n,通常使用.strip()去除。但若输入本身含有空格(如字符串),.strip()会删掉首尾空格,此时用.rstrip('\n')更安全。
import sys
n = int(sys.stdin.readline())
a, b = map(int, sys.stdin.readline().split())
data = list(map(int, sys.stdin.readline().split()))
3. sys.stdin.read() —— 极致速度
- 优点:一次性读入全部内容,速度最快,尤其适合大数据量(10^5以上)。
- 适用:输入量巨大、行数不固定或需要整体解析的场景。
import sys
data = sys.stdin.read().split()
it = iter(data)
n = int(next(it))
arr = [int(next(it)) for _ in range(n)]
输入常用模板
模板1:读取单行多个整数(一行内)
import sys
a, b, c = map(int, sys.stdin.readline().split())
模板2:读取n行,每行一个整数
n = int(sys.stdin.readline())
arr = [int(sys.stdin.readline()) for _ in range(n)]
模板3:读取n行,每行多个整数
n = int(sys.stdin.readline())
matrix = [list(map(int, sys.stdin.readline().split())) for _ in range(n)]
模板4:未知行数,直到EOF
import sys
for line in sys.stdin:
ifnot line.strip():
continue# 跳过空行
a, b = map(int, line.split())
# 处理
或者使用sys.stdin.read().split()一次性处理。
模板5:处理多组测试数据,每组第一行是n,接下来n行
import sys
data = sys.stdin.read().split()
it = iter(data)
whileTrue:
try:
n = int(next(it))
except StopIteration:
break
arr = [int(next(it)) for _ in range(n)]
# 处理当前组
输入常见坑点
- 换行符残留:
sys.stdin.readline() 记得 strip(),否则数字转int时不会报错,但字符串比较可能出问题。 - 空行处理:输入中可能有空行,直接用
line.split() 会得到空列表,用 if not line.strip(): continue 跳过。 - 递归深度:使用DFS时,记得在开头设置
sys.setrecursionlimit(1000000),否则递归超过1000层会报错。
输出:细节决定满分
1. print() 的基本用法
print(a, b, c) # 空格分隔,末尾换行
print(a, b, c, sep=',') # 逗号分隔,末尾换行
print(a, end=' ') # 空格结尾,不换行
2. 格式化输出
使用 join —— 输出列表最优雅
arr = [1, 2, 3]
print(' '.join(map(str, arr))) # 输出 "1 2 3"
- 优点:一次性生成整行,避免循环打印,且行末无多余空格。
使用 f-string —— 灵活直观
name = "Alice"
age = 20
print(f"Name: {name}, Age: {age}")
pi = 3.1415926
print(f"{pi:.2f}") # 保留两位小数 -> 3.14
print(f"{pi:10.2f}") # 宽度10,右对齐 -> " 3.14"
使用 format 或 % (最好备选)
print("{:.2f}".format(pi))
print("%.2f" % pi)
3. 输出性能优化:告别循环 print
当输出行数较多(>1万行)时,循环调用 print 会导致大量IO操作,严重拖慢速度。必须采用批量输出。
低效写法
for i in range(100000):
print(i) # 10万次系统调用,极慢
高效写法
out_lines = []
for i in range(100000):
out_lines.append(str(i))
sys.stdout.write('\n'.join(out_lines))
或者用 io.StringIO 构建大字符串:
import io
out = io.StringIO()
for i in range(100000):
out.write(str(i) + '\n')
sys.stdout.write(out.getvalue())
- 原理:
print 内部会做类型转换和缓冲,且每次调用都涉及系统调用。批量输出只需一次系统调用,速度提升数倍甚至数十倍。
4. 输出格式陷阱清单
| |
|---|
| |
| print() 默认换行,sys.stdout.write 需手动加 \n |
| Python输出 True/False,若题目要求 YES/NO,需手动转换 |
| 使用 f-string 或 format 指定小数位数,避免用 round() |
| 用标志变量控制,第一组前不加空行,组间加 print() |
| |
自查清单
- [ ] 是否使用了
sys.stdin.readline() 或 read() 代替 input() - [ ] 大量输出时是否使用了批量写入(
join 或 StringIO) - [ ] 布尔值大小写是否正确(True/False 还是 YES/NO)
- [ ] 递归函数前是否设置了
sys.setrecursionlimit
作者的话
作者最近编程能力简直灾难性下降,对AI的依赖度越来越高。而且这一年早已不再练习算法,开始转向大型程序开发和前端设计,参加了许多项目、拿了软件专著,但现在让我真刀实枪去比赛算法,真的有些心虚……最后这两周快速拾起一下,以后说不定就没机会再参加编程比赛了。
IO部分是最容易拿分也最容易失分的地方。扎实掌握输入输出,不仅能稳稳拿到基础分,更能为复杂算法留出宝贵的运行时间。接下来两周的复习中,我还会陆续推出基本数据结构、标准库、搜索、动态规划、数论等专题,快速拾起状态。