Python算法竞赛常用技巧与内置库详解

青禾
青禾
发布时间:2026-10-15| 4人浏览过
原创

本文系统梳理Python在算法竞赛与刷题中的常用技巧。涵盖高效输入输出处理、列表与字符串操作、collections模块(deque、Counter、defaultdict)的高级用法、自定义排序逻辑以及itertools排列组合工具,并深入解析可变与不可变类型传参、列表遍历删除及字典安全访问等易错点,帮助开发者提升代码效率与健壮性。

Python算法刷题背景与引言

近年来,Python因其简洁易读的语法迅速走红,已成为众多程序员的首选语言。许多开发者开始将Python作为第一编程语言进行算法题目的练习与求解。

在近期使用Python进行算法训练时,笔者试图寻找类似C++ STL文档那样系统化的Python刷题常用库API与技巧指南,但未果。因此,结合个人实战经验与网络资料,整理出这份文档,供自己回顾及广大同行参考查阅。

输入输出处理技巧

1. 输入与输出处理

1.1 标准输入格式:第一行通常包含两个整数n和m,以空格分隔。其中n表示后续输入的行数,m表示每行包含的数字个数,数字之间同样以空格分隔。

解决方案:Python的input()函数默认接收字符串类型数据。通过字符串分割、强制类型转换以及列表生成器,可以高效解决此类输入问题。具体实现代码如下:


# 接收两个值,使用n,m分别接收列表中的两个值
n, m = [int(x) for x in input().split()]

# 构造输入列表的列表
num_list = []

for i in range(n):
	# python可以不用在意m的值,将所有数值接收进来然后使用len判断长度
	tmp_list = [int(x) for x in input().split()]
	num_list.append(tmp_list)

同理,若数据间以逗号(,)分隔,只需在split()函数中传入逗号即可实现相同效果。

1.2 单行数字输出

由于Python的print()函数默认以换行符作为结束符,若需在同一行输出数字并以空格间隔,需调整end参数。具体代码如下:


for i in range(10):
	print(i, end=' ')

end是print()函数的一个参数,用于指定输出结束时的字符。此处将其修改为空格,即可实现一行数字以空格分隔的输出效果,其他字符可根据需求自行替换。

列表生成、字符串处理与遍历

2. 空列表生成、字符串修改与列表遍历

2.1 代码编写中,常需生成具有特定长度且带有初始值的空列表,生成方式如下:


# 1. 用乘法生成一个初始值为False的长度为100的一维列表
visited = [False] * 100

# 2. 利用列表生成器生成一个n*m的二维的初始值为0的列表
visited = [[0 for i in range(m)] for j in range(n)]

2.2 在Python中,字符串是不可变对象,无法原地修改。若每次修改都生成新字符串,当修改次数频繁且字符串较长时,开销巨大。因此,通常做法是将字符串转换为列表进行修改,最后再拼接回字符串。


string = 'I love to eat chicken'
# 将字符串转换成列表
string_list = list(string)

# .......对字符串列表进行修改
# Code

# 将字符串列表重新拼接成字符串
#string = ''.join(string_list)

2.3 Python中列表遍历有多种方式。最直接的是直接迭代列表元素,但由于算法题常需基于索引操作数组并修改值,更推荐以下代码中的后两种方法:


num_list = [i for i in range(10)]

# 1. 直接迭代列表
for item in num_list:
	# Code
	pass

# 2. 通过索引进行迭代
for i in range(len(num_list)):
	print(num_list[i])

# 3. 通过enumerate函数进行迭代
for index, value in enumerate(num_list):
	# index为当前元素的索引,value为当前元素的值
	print(index, value)

collections库核心组件详解

3. collections库的高级应用

3.1 deque双端队列

deque是Python中的队列实现(FIFO,先进先出),在队首执行弹出操作时,其性能优于普通list。

特别是在使用BFS(广度优先搜索)算法时,队列是核心数据结构。部分deque使用示例代码如下:


from collections import deque

# 初始化一个最大长度为3的队列
d = deque([1,2,3], maxlen=3)

# 因为初始化队列最大长度为3,再添加元素会把队头元素挤出
d.append(4)

# 初始化一个不限制长度的队列
d = deque()

# 添加元素到队尾部
d.append(1)
d.append(2)
d.append(3)

# 将队首的元素弹出返回
print(d.popleft())

# 弹出队尾元素并返回值
print(d.pop())

# 在队首插入元素
d.appendleft(0)

3.2 Counter计数器

Counter是一个计数器类,可用于统计序列中各元素出现的频次。

以下是具体示例代码:


import collections

# 一共有三种初始方法
# 1. 传入一个序列
print(collections.Counter(['a', 'b', 'c', 'a', 'b', 'b']))
# 2.传入一个字典
print(collections.Counter({'a':2, 'b':3, 'c':1}))
# 3.直接利用=传参
print(collections.Counter(a=2, b=3, c=1))

# 也可以无参数构造,利用update函数更新
c = collections.Counter()
print('Initial :', c)
# Initial: Counter()


c.update('abcdaab')
print('Sequence:', c)
# Sequence: Counter({'a': 3, 'b': 2, 'c': 1, 'd': 1})


c.update({'a':1, 'd':5})
print('Dict:', c)
# Dict: Counter({'d': 6, 'a': 4, 'b': 2, 'c': 1})

# 可以通过访问字典的访问方式访问Counter对象
for letter in 'abcde':
 print('%s : %d' % (letter, c[letter]))

# elements()方法可以返回一个包含所有Counter数据的迭代器
c = collections.Counter('extremely')
c['z'] = 0
print(list(c.elements()))
# ['e', 'e', 'e', 'm', 'l', 'r', 't', 'y', 'x']

# most_common()返回前n个最多的数据
c=collections.Counter('aassdddffff')
for letter, count in c.most_common(2):
 print('%s: %d' % (letter, count))
# f: 4
# d: 3

# Counter对象可以进行加减交并,直接使用运算符 +、-、&、|
# +会将两个字典中相同字符的出现次数相加,-会给出第一个Counter相对于第二个的差集。交集给出两个计数器当中都有的元素,且计数被赋值为较小的那个,并集为两个计数器的元素出现最多的那个。

c1 = collections.Counter(['a', 'b', 'c', 'a', 'b', 'b'])
c2 = collections.Counter('alphabet')

print ('C1:', c1)
print ('C2:', c2)

print ('\nCombined counts:')
print (c1 + c2)

print ('\nSubtraction:')
print (c1 - c2)

print ('\nIntersection (taking positive minimums):')
print (c1 & c2)

print ('\nUnion (taking maximums):')
print (c1 | c2)

# 以下为输出:
C1: Counter({'b': 3, 'a': 2, 'c': 1})
C2: Counter({'a': 2, 'l': 1, 'p': 1, 'h': 1, 'b': 1, 'e': 1, 't': 1})

Combined counts:
Counter({'a': 4, 'b': 4, 'c': 1, 'l': 1, 'p': 1, 'h': 1, 'e': 1, 't': 1})

Subtraction:
Counter({'b': 2, 'c': 1})

Intersection (taking positive minimums):
Counter({'a': 2, 'b': 1})

Union (taking maximums):
Counter({'b': 3, 'a': 2, 'c': 1, 'l': 1, 'p': 1, 'h': 1, 'e': 1, 't': 1})

3.3 defaultdict——带默认值的字典

常规创建的dict字典不含默认值,若访问不存在的key(如dct[a]),程序将抛出错误。

在算法与数据结构设计中,往往希望任意给定key都能从字典中获取值(即使只是默认值)。此时,需使用defaultdict来实现这一功能。

例如,在构建图的邻接表表示时,希望以节点为key,相连节点列表为value。此时可使用defaultdict(list)创建默认值为空列表的字典,简化代码逻辑。


# list的默认值为空列表
list_dict = collections.defaultdict(list)
# int的默认值为0
int_dict = collections.defaultdict(int)

print(list_dict['a'])
print(int_dict['a'])

# 输出:[]
# 输出:0

3.4 小结

collections库中,常用于算法与数据结构实现的即为上述几种。其他如排序字典(OrderedDict)和命名元组(namedtuple)在常规算法题中较少使用。

列表与字典排序策略

4. 排序技巧

4.1 列表排序

Python列表排序主要有两种方法:一是使用列表内置的sort()函数,该函数直接在原列表上进行修改,无返回值,但可通过key参数自定义比较键或比较函数。

二是使用内置函数sorted(),该函数自由度更高,可自定义比较函数和key,并返回一个新的排序后列表。

若需自定义比较函数,需从functools库导入cmp_to_key函数,将比较函数转换为key参数。使用代码如下:


def custom_sort(x,y):
 if x>y:
 	# 返回-1代表需要排在前面
  return -1
 if x<y:
 	# 返回1代表需要排在后面
  return 1
 return 0


lst = [i for i in range(10, -1, -1)]
print(lst)

lst.sort()
print(lst)

print(sorted(lst, key=cmp_to_key(custom_sort)))

# 输出如下:
# [10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
# [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
# [10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]

4.2 字典/元组列表排序

若需对字典(通过item()函数转化为元组列表)或每个元素包含两个值的元组列表进行排序,需利用sorted()函数的key参数指定排序依据的值。代码如下:


# 利用字符串创建计数器字典
d = dict(collections.Counter('Hello World'))
print(d)
# 排序
print(sorted(d.items(), key=lambda x: x[1], reverse=True))

# 输出如下:
# {'H': 1, 'e': 1, 'l': 3, 'o': 2, ' ': 1, 'W': 1, 'r': 1, 'd': 1}
# [('l', 3), ('o', 2), ('H', 1), ('e', 1), (' ', 1), ('W', 1), ('r', 1), ('d', 1)]

itertools排列组合应用

5. 排列与组合

Python内置模块itertools集成了一系列与迭代器相关的函数,其中包括排列与组合功能。

5.1 排列

排列函数permutations接收一个可迭代对象,返回该对象内所有元素的全排列列表。


from itertools import permutations
print(list(permutations([1,2,3])))

# 输出如下:
# [(1, 2, 3), (1, 3, 2), (2, 1, 3), (2, 3, 1), (3, 1, 2), (3, 2, 1)]

5.2 组合

组合函数combinations接收两个参数:第一个为待组合的可迭代对象,第二个为正整数,表示从列表中抽取的元素个数,返回所有可能的组合列表。


from itertools import combinations
print(list(combinations([1,2,3],2)))

# 输出如下:
# [(1, 2), (1, 3), (2, 3)]

函数传参与列表操作避坑指南

6. 实用编程技巧

6.1 Python区分可变类型与不可变类型,函数传参行为如下:

  • 对于不可变类型(如字符串),传参时会进行深拷贝,在副本上修改不影响原数据;对于可变类型(如列表),传参时传递的是引用(浅拷贝),在函数内部对列表的操作会影响原列表。
  • 若确实需要在函数内修改传入的可变类型而不影响外部,可在函数第一行进行深拷贝,例如:

def test(num_list:list):
	# 进行深拷贝
	num_list = num_list[:]

6.2 遍历列表时删除元素易导致索引错误

例如,列表为[1,2,3,4,5,6],若需删除所有偶数,直接通过索引查找并删除(如下错误示范),会导致后续元素前移,引发索引越界或漏删问题。


# 此处为错误示范!!!!!!!!
lst = [1, 2, 3, 4, 5, 6]
for i in range(len(lst)):
 if lst[i] % 2 == 0:
  lst.pop(i)

print(lst)

# 上面这段代码没有输出,因为报索引越界错误了。

正确的处理方式如下:


lst = [1, 2, 3, 4, 5, 6]
lst = [i for i in lst if i % 2 != 0]

print(lst)

# 输出如下:
# [1, 3, 5]

若需更复杂的筛选逻辑,可在if i%2 !=0处替换为判断函数,在函数内部实现具体的筛选逻辑。

6.3 安全访问字典元素

前文提及,普通dict字典无默认值,直接通过中括号键值查找value可能引发KeyError。

为避免此类错误,在通过key获取value时,建议使用字典的get()方法。

get()方法的第一个参数为key,第二个参数为可选值(默认为None)。当字典中不存在该key时,将返回第二个参数指定的默认值。

总结与展望

7. 总结

以上总结自笔者在Python算法刷题过程中的实践经验,若有不足之处,欢迎指正。

至此,关于Python算法常用技巧与内置库的详解介绍完毕。更多相关Python算法技巧与内置库内容,欢迎搜索本站往期文章或继续浏览后续文章。感谢大家的支持!

相关标签:

免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。

Python 相关产品和应用

全部下载
Selenium

Selenium

4.51.0 · Windows

用于浏览器自动化和 Web 测试的 Python 工具。

scikit-learn

scikit-learn

1.9.1 · Windows

常用 Python 机器学习算法与模型评估工具库。

Black

Black

26.10.1 · Windows

强调一致性的 Python 自动代码格式化工具。

Ruff

Ruff

0.17.1 · macOS

由 Rust 编写的高速 Python 代码检查和格式化工具。

pytest

pytest

9.1.1 · Windows

简洁而强大的 Python 自动化测试框架。

FastAPI

FastAPI

0.146.0 · Windows

高性能 Python API 框架,支持类型提示和自动接口文档。

Flask

Flask

3.1.3 · Windows

轻量灵活的 Python Web 应用框架。

Django

Django

6.1.2 · Windows

成熟的 Python Web 框架,适合构建内容站点和业务系统。

Spyder

Spyder

6.1.7 · Windows

面向科学计算与数据分析的 Python 集成开发环境。

SciPy

SciPy

1.18.1 · Windows

面向科学、工程和技术计算的 Python 算法库。