最近在复刷LeetCode,某种意义上做题要做得快,对STL保持熟悉度是蛮重要的,至少在求和、排序、查找时就不用重复敲代码了。于是,整理了部分常用python算术/数据结构/数理模块,以及相应的对TLE敏感的操作,希望能帮助到其它人。
- Python标准库
format()
:格式化输出函数(在控制输出精度和空格补齐等问题中很好用)round(x)
:四舍五入cmp(x, y)
:比较2个对象,如果 x < y x < y x<y 返回 − 1 -1 −1, 如果 x = y x=y x=y 返回 0 0 0 , 如果 x > y x > y x>y 返回 1 1 1abs(x)
/max()
/min()
:绝对值/最大值/最小值len()
:返回对象的长度,如列表、字典等range(start=0, stop, step=1])
:返回一个可迭代对象,常用于for
循环sum(iterable)
: 求和函数pow(x, y, [z])
:求幂函数 x y x^y xy,运算完毕可以顺带对z
取模;大数取模问题可以直接ACsorted(iterable, key, reverse)
:采用Timsort的稳定排序算法,默认升序;该函数太重要了,请多写写代码研究一下它的各个参数isinstance(obj, type)
:判断类型all(iterable)
/any(iterable)
:迭代与操作/迭代或操作(0
、1
、None
均等价于False
)int(x, base=10))
/float()
/str()
:转整数(可自定义进制)/转浮点数/转字符串bin()
/oct()
/hex()
:10进制转二进制(返回0b
开头的字符串)/10进制转八进制(返回0
开头的字符串)/10进制转十六进制(返回0x
开头的字符串)ord()
/chr()
:字符转ASCII或ASCII转字符complex(real, imag)
:创建一个复数,不过现在可以直接通过语法糖创建,eg:1+2j
divmod()
:函数把除数和余数运算结果结合起来,返回一个包含商和余数的元组(a // b, a % b)
eval(expression, [globals, locals])
:执行一个字符串表达式,并返回表达式的值,eg:eval('pow(2, 2)')
= 4 =4 =4;在某些字符串解析题中非常好用,详细可见>此处<map(function, iterable...)
:映射函数,需要注意的是在Python3中返回一个迭代对象而不再是一个列表filter(function, iterable)
:过滤函数reduce(function, iterable, [initializer])
:累积函数,现在被归类到functools
模块中zip(iterable, ...)
:将对象中对应的元素打包成一个个元组,常用于并联取值,详见下文的【注意事项】hash(obj)
:返回哈希码,常用于对象比较或将其转化为唯一的索引(不过更推荐使用id()
完成该需求)id()
:返回对象的唯一标识符,和hash()
类似,但更鲁棒更快且可以对list
、def
等对象求idenumerate()
:将一个可遍历的数据对象组合为一个索引序列,eg:for i, v in enumerate(list_a):
>参考资料:完整列表<
注意事项:
print(*objects, sep=' ', end='\n')
可以通过设置seq
标志位来设定间隔字符、设置end
来设定结尾字符,常用于进行灵活的输出。对列表输出时,也常用' '.join(result_list)
来快速输出一个串,而不用挨个遍历。range()
中取遍历指示器,在Python 3中,在for循环内重复定义迭代变量不影响迭代遍历的值,如:# (Python 3)该段代码最终输出0 1 2三行,而不是只有一行0
for i in range(3): # 并不等价于C语言的 for(int i=0;i<3;i++)
print(i)
i += 10 # 内部遍历不影响作为循环i的值,这跟C语言差异很大,请留心
map
、filter
、reduce
等函数式编程函数很重要,能够让你少敲很多代码,特别是filter
在筛选题中有高的实用性。float()
可用来构造无限值,如float('inf')
。yield
关键字也很重要,可用来创建生成器,在数列生成、斐波那契查找等用处很大,可以极大的节省开销。>举例一则<,>教程指南<。需要注意的是,当迭代器函数执行结束时,将自动抛出StopIteration
异常,仅在 for 循环里,无需处理StopIteration
异常,循环会正常结束,否则需要使用except
关键字捕获。try
、except Exception as e
、finally
等语句可以直接避免在函数体里进行空值检定,大大减少敲键盘的时间开销:>举例一则<。set()
来进行列表去重。format()
函数能方便你的格式化输出,如小数精度控制问题,请考虑>进行学习<。input()
和raw_input()
的区别在于对于python的两个版本(2/3)表现不一致,python 3请使用前者,接收到的数据默认为str
。list
)变量,需要用global
关键词在函数内使用全局变量。del
关键词会比list.pop(index)
拥有更高的效率,属于TLE敏感的操作。list[::-1]
语法糖可以快速对列表进行倒序,在字符串倒序等问题中有奇效;但list.reverse()
更高效,属于TLE敏感的操作。(另外需要注意:后者是原地倒序)zip()
返回一个可迭代对象,需要手动list()
以进行其它列表操作。statistics
模块中;求众数可以调用collections.Counter
来实现。maximum recursion depth exceeded in comparison
。常量:
pi
:圆周率 (3.141592653589793
)inf
:无限大nan
:非数字(Not A Number)e
:自然数tau
:圆周率的两倍,用于计算弧度和角度的换算。( 2 π × 2π \times 2π×弧度 = 360 ° =360° =360°)常用方法:
floor(x)
:向下取整,eg: f l o o r ( 35.7 ) = 35 floor(35.7)=35 floor(35.7)=35ceil(x)
:向上取整,eg: f l o o r ( 35.1 ) = 36 floor(35.1)=36 floor(35.1)=36trunc(x)
:直接去除数字的小数部分,作用等同floorgcd(a, b)
:求最大公约数,返回值默认与 b b b同号(b非0时);任一数为 0 0 0时返回 0 0 0;pow(x, y)
:求幂( x y x^y xy),返回浮点数,eg: m a t h . p o w ( 2 , 3 ) = 8.0 math.pow(2, 3)= 8.0 math.pow(2,3)=8.0log(x, y)
:求对数 l o g y x log_yx logyx,返回浮点数,eg: m a t h . l o g ( 8 , 2 ) = 3.0 math.log(8, 2) = 3.0 math.log(8,2)=3.0log2(x)
/log10(x)
/log1p(x)
:以 2 2 2/ 10 10 10为底的对数;求自然对数 l n ( x + 1 ) ln(x+1) ln(x+1)radians
:将角度转换为弧度sin
/sinh
:正弦cos
/cosh
:余弦tan
/tanh
:正切sqrt(x)
:平方根modf(x)
:返回浮点数 x x x的小数和整数部分,eg: m o d f ( 32.5 ) = ( 0.5 , 32 ) modf(32.5)=(0.5, 32) modf(32.5)=(0.5,32)exp(x)
:指数函数 e x e^x ex,需要注意的是math.pow(math.e, x)
精度会更高isnan
/isinf
/isfinite
:常量检测函数,isfinite
当检测数为inf
和nan
时返回False注意事项:
math.fabs()
和内建函数中的abs()
相比,后者支持复数运算。复数的绝对值计算: ∣ a ± b j ∣ = a 2 + b 2 |a±bj|=\sqrt{a^2+b^2} ∣a±bj∣=a2+b2。divmod(a, b)
与math.modf(x)
相比,前者返回元组(a // b, a % b)
,后者拆分小数和整数部分;前者支持复数运算。math.pow()
和内建函数中的pow()
相比,前者运算为浮点型,后者会保留整形(还可以顺带取模)fractions.gcd()
在Python 3后已被math.gcd()
取代。(a * b) // gcd(a, b)
。复数运算模块,大部分API与math相同。
Python中构建复数的语法示例为:c = 1 + 1j
,如:
a, b = 1+1j, 1-1j
print(a, b, a+b) # (1+1j) (1-1j) (2+0j)
二分查询算法模块,时间复杂度 O ( n ) = l o g n O(n)=log_n O(n)=logn。常用于有序序列(升序)的插入和查找。默认使用bisect_right()
、insort_right()
。
bisect_left(a, x, lo=None, hi=None)
/bisect_right()
:在有序列表的 [ l o , h i ) [lo, hi) [lo,hi)a
中查找值x
并返回其索引,有左向和右向之分,分别当命中时返回左/右一位的索引,即bisect_left -> all e in a[i:] have e >= x
、bisect_right -> all e in a[i:] have e > x
。insort_left(a, x, lo=None, hi=None)
/insort_right()
:在有序列表的 [ l o , h i ) [lo, hi) [lo,hi)a
中插入值x
并保证顺序。a = [0, 1, 2, 3, 5, 6, 8, 17]
print(bisect.bisect_left(a, 4)) # 4
print(bisect.bisect_right(a, 4)) # [default] 4
print(bisect.bisect_left(a, 5)) # 4
print(bisect.bisect_right(a, 5)) # [default] 5
数据统计模块,补全了中位数、众数、方差的快速实现。同时支持下文的分数模块。
mean()
: 数据的算术平均数(“平均数”)harmonic_mean()
|:数据的调和均值median()
:数据的中位数(中间值)median_low()
:数据的低中位数median_high()
:数据的高中位数mode()
:数据的众数pstdev()
:数据的总体标准差pvariance()
:数据的总体方差stdev()
:数据的样本标准差variance()
:数据的样本方差分数运算模块,>参考此处<。
构造方法:
class fractions.Fraction(numerator=0, denominator=1)
class fractions.Fraction(other_fraction)
class fractions.Fraction(float)
class fractions.Fraction(decimal)
class fractions.Fraction(string)
常用方法:
gcd(a, b)
:求最大公约数,返回值默认与 b b b同号(b非0时);任一数为 0 0 0时返回 0 0 0注意事项:
fractions.gcd()
在python3后已被math.gcd()
取代,此处的API仅对python 2有效。堆模块,构建小顶堆。构建时直接传入一个列表作为堆结构,调用heapify
等函数时会直接改变这个堆列表的内部顺序。
小顶堆:根节点的值小于等于其左右节点的值,即
a[k] <= a[2*k+1] and a[k] <= a[2*k+2]
heapify(heap)
:构建/调整一个堆heappop(heap)
:返回最小数heappush(heap, item)
:向堆内压入一个元素heappushpop(heap, item)
:向堆内压入一个元素后返回最小数heapreplace(heap, item)
:删除堆中最小元素并加入一个元素merge(*iterables)
:合并多个有序列表,并返回有序列表的迭代器(即需要手动list()
)nlargest(n, iterable, key)
:返回最大的n个数的列表nsmallest(n, iterable, key)
:返回最小的n个数的列表技巧:如果要构建大顶堆,对入堆元素取负即可,如:
heap = [1, 2, 3, 4, 5]
heap = [-i for i in heap]
heapify(heap)
m = -heappop(heap) # 注意取负回来
另外需要注意,堆排序是不稳定的。
队列模块,Queue
其实不是很必要,完全可以用List
的内建函数替代;比较有用的是其中的PriorityQueue
,常用于解图结构相关的问题,如有向图最短距离等。特殊的,还有内建对象deque
作为双向队列,本质是封装了collections.deque
,此除不做讲解。
需要注意是的,queue
模块中提供的是同步的类,因此其入列/出列运算速度是比较慢的,远不及list.insert()
和list.pop()
。
类:
Queue(maxsize=0)
FIFO 队列。如果 maxsize 小于等于零,队列尺寸为无限大,下同。
LifoQueue(maxsize=0)
LIFO 队列。
PriorityQueue(maxsize=0)
优先级队列。
优先级队列:二叉堆,本质上就是一个小顶堆/大顶堆。
通用API:
qsize()
:返回队列大小full()
:队列是否满了put(item, block, timeout)
:以同步的方式入列get(item, block, timeout)
:以同步的方式出列put_nowait(item)
:以非同步的方式入列get_nowait(item)
:以非同步的方式出列优先队列的
item
应该为以下数据格式:(priority number, data)
是Python内建的一个集合模块,提供了许多有用的集合类。该模块较为重要,为本文行文流畅考虑,此处仅做简介索引和用方举例。详细了解API可到:>扩展学习资料<
常用类:
namedtuple
:构建一个可命名的tuple
,该类型可用isinstance
检定deque
:高效实现插入和删除操作双向列表,比list
优异,可直接改造成FIFO队列或栈,API与list
基本相同ChainMap
:将多个dict串成一个逻辑上的dict
(不修改内存),往往用于优先级查找Counter
: 计数器,大部分情况下速度会更快,但若只统计个别元素,推荐使用list.count()
,接受一个字典或字符串OrderedDict
: 有序字典(按key
的值升序排序),常用来做一个FIFO的有序字典defaultdict
: 访问缺失值时会返回一个默认值的字典而不是抛出KeyError
,相当好用,其它行为与dict
一致,可以看成是一种更健壮的dict
该类由于重载了加、减、del等运算,因此可以直接进行减操作,在对字符串进行统计时想当有用。
构造:
Counter()
:空构建Counter(st)
:传入一个字符串Counter(dict)
:传入一个字典Counter(key=v, ...)
:传入一些键值对常用方法:
update(Counter)
:most_common(n)
:返回 最多的n个元素的列表当只对首位数据进行操作时,双向队列类比list
有着更高的效率。其与list
相比,用有几乎完全相同的API,只不过pop()
不允许按index
弹出数据,只能弹出队尾数据,并且多了以下API:
popleft()
:与pop()
相对,从尾部弹出数据(没有发现存在比pop(0, data)
明显的性能优势)appendleft()
:与append()
相对,向首部添加数据(没有发现存在比insert(0, data)
明显的性能优势)注意事项:
collections.deque
比queue.deque
更加强大;后者是对前者的封装,作为模块内建对象存在,而前者这是一个类。后者拥有dequeue
、enqueue
等标准队列操作,但个人更推荐使用前者而不是后者。
高阶函数模块,对于数学题或函数式编程风格优化起到重要作用。
主要方法:
reduce(function, iterable, [initializer])
:累积函数,常用于列表连续求积其它方法跟装饰器有关,不一一罗列。
本模块实现了一系列 iterator,比起手动for
循环,会快很多。
无穷迭代器:
count(start, [step])
:步进迭代器,生成序列start, start+step, start+2*step...
cycle(iterable)
:循环迭代器,输入迭代器p
,生成序列p0, p1, ... plast, p0, p1, ...
repeat(obj [,n])
: 重复迭代器,重复无限次或n次,生成序列obj, obj, obj, ...
有限迭代器:
accumulate(iterable, [func])
:创建一个迭代器,根据func
返回累加和或其他二元函数的累加结果。eg:accumulate([1,2,3,4,5]) --> 1 3 6 10 15
chain
:创建一个迭代器,它首先返回第一个可迭代对象中所有元素,接着返回下一个可迭代对象中所有元素,直到耗尽所有可迭代对象中的元素,eg:chain('ABC', 'DEF') --> A B C D E F
compress(data, selectors)
:返回 data 中经 selectors 真值测试为 True 的元素,eg:compress('ABCDEF', [1,0,1,0,1,1]) --> A C E F
dropwhile
:从首次真值测试失败开始,eg:dropwhile(lambda x: x<5, [1,4,6,4,1]) --> 6 4 1
takewhile
:直到首次真值测试失败结束,eg:takewhile(lambda x: x<5, [1,4,6,4,1]) --> 1 4
filterfalse(predicate, iterable)
:返回iterable
中predicate
为False
的元素,eg:`filterfalse(lambda x: x%2, range(10)) --> 0 2 4 6 8groupby(iterable, [key])
:迭代器中相邻的重复元素挑出来放在一起,eg:for i, v in itertools.groupby('AABBC'): print(i, list(v)) --> A ['A', 'A'] B ['B', 'B'] C ['C']
islice(iterable, start, stop[, step])
:返回从 iterable 里选中的元素,eg:islice('ABCDEFG', 2, None) --> C D E F G
starmap(function, iterable)
:星映射迭代器,eg:starmap(pow, [(2,5), (3,2), (10,3)]) --> 32 9 1000
tee(iterable, n)
:拆分一个迭代器为 n n n个zip_longest
:创建一个迭代器,从每个可迭代对象中收集元素。如果可迭代对象的长度未对齐,将根据fillvalue
填充缺失值,eg:zip_longest('ABCD', 'xy', fillvalue='-') --> Ax By C- D-
排列组合迭代器:
product
:笛卡尔积,eg:product('ABCD, 2') --> AA AB AC AD BA BB BC BD CA CB CC CD DA DB DC DD
permutations
:连续返回由 iterable 元素生成长度为 r 的排列,和combinations
有点类似,即返回组合排列解,eg:permutations('ABCD, 2') --> AB AC AD BA BC BD CA CB CD DA DB DC
combinations
:返回由输入iterable
中元素组成长度为 r r r的子序列,eg:combinations('ABCD', 2) --> AB AC AD BC BD CD
注意事项:
accumulate
可通过给定一个func
来自定义累加过程,例如给定max
作为累加器,则统计的是至今为止最大的数,在一些题目中相当好用。combinations
本质上就是就是计算组合数,在一些数学题中相当好用,例如print(len(list(itertools.combinations('ABCD', 2))))
可以直接得到 C 4 2 = 6 C_4^2=6 C42=6;permutations
则是返回 A 2 2 C 4 2 A_2^2C_4^2 A22C42。专业导向的模块除了
re
以外,一般不怎么用到,只对于某些专业题目,如二进制、文件、时间、加密等。
时间模块,可以直接对日期进行加减等操作,在某些日期问题中非常有用。如二月份的日期加减:
print(datetime.date(2019, 2, 28) + datetime.timedelta(days=1))
# 2019-03-01
常用类:
date(year, month, day)
:日期模型time(hour, minute, second, microsecond, tzinfo)
:时间模型datetime(...)
:日期和时间的结合模型;常用timestamp()
可将其转为时间戳timedelta(days, seconds, microseconds, milliseconds, minutes, hours, weeks)
:时间增量,常用total_seconds()
可以将其转化为秒。timezone
:时区类主要用于构建日历,在诸如打印出某年第某月日历的题目时非常有用,如打印2020年第二个月的日历(注意并不是2月1号到2月29号,而是印刷日历的编号,即为1月27日到3月1日【打开你右下角的windows日历自行查查对】)
c = calendar.Calendar(firstweekday=0) # 0:日历印刷的第一天为星期一
for i in c.itermonthdates(2020, 2):
print(i)
struct
模块来解决bytes
和其他二进制数据类型的转换。常用于处理二进制或字符串题。
>简易入门<
format-characters
在这些专业导向的模块中,正则模块则是最重要的模块,能处理大量字符串相关的题目,需要额外学习正则表达式,此处不再赘述,属于必须掌握的模块。请参考>本文档<进行学习。
主要用于处理浅层和深层拷贝,在某些需要自定义数据结构的题目中能方便你进行对象拷贝,属于必须掌握的模块。主要只有两个函数:
copy
:返回浅层拷贝。deepcopy
:耗时大,返回深层拷贝对象。在一些路径字符串分析题中非常有用,大部分情况下可以用re
模块和str
替代。
主要掌握str
模块类的以下方法:
strip([chars])
:返回原字符串的副本,移除其中的前导和末尾字符;常用于IOsplit(sep=None, maxsplit=-1)
:返回一个由字符串内单词组成的列表,常用于IOfind(sub, [start], [end])
:返回子字符串 sub 在s[start:end]
切片内被找到的最小索引count(sub, [start], [end])
:反回子字符串 sub 在 [start, end] 范围内非重叠出现的次数。center(width, [fillchar])
:返回长度为 width 的字符串,原字符串在其正中, 使用指定的 fillchar 填充两边的空位(优先填右)capitalize()
:返回将首字母大写、其余小写的原字符串副本index(sub, [start], [end])
:类似find()
,不推荐使用,因为找不到时会触发一个异常replace(old, new, [count])
:替换子串,不过基本都由re.sub()
替代该操作join(iterable)
:返回一个由 iterable 中的字符串拼接而成的字符串;常用于IOlower()/upper()
:返回一个副本,将所有字符转为小/大写lower()/isupper()
:判断是否全由小写字母/大写字母组成isdigit()/isdecimal()/isnumeric()
:判断是否全由数字字母组成【区别见下文】isalnum()/isalpha()
:判断是否全由字母和数字组成/判断是否全由字母组成translate(table)
:映射字符串,需要搭配str.maketrans(dict)
一起用;eg: print("abc".translate(str.maketrans({"a": "1"})))
将会输出1bc
title()
:转化为标题形式,仅用于一些针对性的题目注意事项:
list
,可以进行切片或使用s[::-1]
的语法糖进行倒序,但不能s[i]="x"
进行修改性赋值(请使用str.replace(...)
或将其转为一个list
)isdigit()/isdecimal()/isnumeric()
的区别使用场景为:True | False | Error | |
---|---|---|---|
isdigit | Unicode数字,byte数字(单字节),全角数字(双字节),罗马数字 | 汉字数字 | / |
isdecimal | Unicode数字,,全角数字(双字节) | 罗马数字,汉字数字 | / |
isnumeric | Unicode数字,全角数字(双字节),罗马数字,汉字数字 | / | byte数字(单字节) |
此模块提供了将二进制数据编码为可打印的 ASCII 字符以及将这些编码解码回二进制数据的函数,在某些数据分析题中很有用,但比较冷门,偶尔会遇到,如果掌握了可以节省大量的时间。
此模块提供用于比较序列的类和函数,在算法题中属于冷门模块,在一些用于比较字符串差异的题目中可以进行使用,例如比较 两个字符串序列的前后差异:
c = difflib.ndiff("abcd", "abde")
for i in c:
print(i)
上文中提到了部分敏感操作,接下来继续总结一些:
x in obj
要比obj.find(x)
效率高,如str
或list
查找时。list
进行扩增,以下三种方式的效率依次增高:for _ in range(j): li.append(x)
<li[i:i+j] = [x]*j
<li += [x]*j
。del x
要比list.pop(index_x)
效率高。list.reverse()
要比list[::-1]
效率高。[[0 for i in range(1000)] for j in range(1000)]
要比[[0] * 1000 for j in range(1000)]
效率高,但不推荐直接开一个特别大的数组,该操作本身非常耗时间。li += [1, 2, 3]
要比li.extend([1, 2, 3])
效率高,但在扩增的数据量较少时,候没有绝对优势。s.remove(x)
要比s.pop(s.index(x))
或del s[s.index(1)]
有性能优势,如果非必要,请直接移除。相对的,还有以下性能比较:if x in s: s.remove(x)
>del s[s.index(x)]
>s.pop(s.index(x))
;总而言之,若对索引不感兴趣,尽量用in
关键字做包含性查询、用del
关键字移除对象。