引言
最近在復刷LeetCode,某種意義上要做題做的快對STL保持熟悉度是蠻重要的,至少在求和、排序、查找時就不用重複敲代碼了。於是,整理了部分常用python算術/數據結構/數理模塊,以及相應的對TLE敏感的操作,希望能幫助到其它人。
文章目錄
python內建函數
format()
:格式化輸出函數(在控制輸出精度和空格補齊等問題中很好用)round(x)
:四捨五入cmp(x, y)
:比較2個對象,如果 返回$ -1x==y$返回, 如果返回abs(x)
/max()
/min()
:絕對值/最大值/最小值len()
:返回對象的長度,如列表、字典等range(start=0, stop, step=1])
:返回一個可迭代對象,常用於for
循環sum(iterable)
: 求和函數pow(x, y, [z])
:求冪函數,運算完畢可以順帶對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)')
;在某些字符串解析題中非常好用,詳細可見[>此處<],(https://www.runoob.com/python/python-func-eval.html)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敏感的操作。(另外需要注意:後者是原地倒序)- 在python 3中,
zip()
返回一個可迭代對象,需要手動list()
以進行其它列表操作。 - 求均值、中位數、衆數的API在
statistics
模塊中;求衆數可以調用collections.Counter
來實現。 - python默認的遞歸棧很有限,有時候會出現C語言能過而python不能過的情況,請>參考此處<解決這個問題——
maximum recursion depth exceeded in comparison
。
算術模塊
1. math
常量:
pi
:圓周率 (3.141592653589793
)inf
:無限大nan
:非數字(Not A Number)e
:自然數tau
:圓周率的兩倍,用於計算弧度和角度的換算。( 弧度)
常用方法:
floor(x)
:向下取整,eg:ceil(x)
:向上取整,eg:trunc(x)
:直接去除數字的小數部分,作用等同floorgcd(a, b)
:求最大公約數,返回值默認與同號(b非0時);任一數爲時返回;pow(x, y)
:求冪( ),返回浮點數,eg:log(x, y)
:求對數,返回浮點數,eg:log2(x)
/log10(x)
/log1p(x)
:以/爲底的對數;求自然對數radians
:將角度轉換爲弧度sin
/sinh
:正弦cos
/cosh
:餘弦tan
/tanh
:正切sqrt(x)
:平方根modf(x)
:返回浮點數的小數和整數部分,eg:exp(x)
:指數函數,需要注意的是math.pow(math.e, x)
精度會更高isnan
/isinf
/isfinite
:常量檢測函數,isfinite
當檢測數爲inf
和nan
時返回False
注意事項:
math.fabs()
和內建函數中的abs()
相比,後者支持複數運算。複數的絕對值計算:。divmod(a, b)
與math.modf(x)
相比,前者返回元組(a // b, a % b)
,後者拆分小數和整數部分;前者支持複數運算。math.pow()
和內建函數中的pow()
相比,前者運算爲浮點型,後者會保留整形(還可以順帶取模)fractions.gcd()
在Python 3後已被math.gcd()
取代。
2. cmath
複數運算模塊,大部分API與math相同。
Python中構建複數的語法示例爲:c = 1 + 1j
,如:
a, b = 1+1j, 1-1j
print(a, b, a+b) # (1+1j) (1-1j) (2+0j)
3. bisect
二分查詢算法模塊,時間複雜度。常用於有序序列(升序)的插入和查找。默認使用bisect_right()
、insort_right()
。
bisect_left(a, x, lo=None, hi=None)
/bisect_right()
:在有序列表的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()
:在有序列表的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
4. statistics
數據統計模塊,補全了中位數、衆數、方差的快速實現。同時支持下文的分數模塊。
mean()
: 數據的算術平均數(“平均數”)harmonic_mean()
|:數據的調和均值median()
:數據的中位數(中間值)median_low()
:數據的低中位數median_high()
:數據的高中位數mode()
:數據的衆數pstdev()
:數據的總體標準差pvariance()
:數據的總體方差stdev()
:數據的樣本標準差variance()
:數據的樣本方差
5. fractions
分數運算模塊,>參考此處<。
構造方法:
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非0時);任一數爲時返回
注意事項:
fractions.gcd()
在python3後已被math.gcd()
取代,此處的API僅對python 2有效。
數據結構
6. heapq
堆模塊,構建小頂堆。構建時直接傳入一個列表作爲堆結構,調用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) # 注意取負回來
另外需要注意,堆排序是不穩定的。
7. queue
隊列模塊,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)
8. collections
是Python內建的一個集合模塊,提供了許多有用的集合類。該模塊較爲重要,爲本文行文流暢考慮,此處僅做簡介索引和用方舉例。詳細瞭解API可到:>擴展學習資料<
常用類:
namedtuple
:構建一個可命名的tuple
,該類型可用isinstance
檢定deque
:高效實現插入和刪除操作雙向列表,比list
優異,可直接改造成FIFO隊列或棧,API與list
基本相同ChainMap
:將多個dict串成一個邏輯上的dict
(不修改內存),往往用於優先級查找Counter
: 計數器,大部分情況下速度會更快,但若只統計個別元素,推薦使用list.count()
,接受一個字典或字符串OrderedDict
: 有序字典(按key
的值升序排序),常用來做一個FIFO的有序字典defaultdict
: 訪問缺失值時會返回一個默認值的字典而不是拋出KeyError
,相當好用,其它行爲與dict
一致,可以看成是一種更健壯的dict
8.1 Counter
該類由於重載了加、減、del等運算,因此可以直接進行減操作,在對字符串進行統計時想當有用。
構造:
Counter()
:空構建Counter(st)
:傳入一個字符串Counter(dict)
:傳入一個字典Counter(key=v, ...)
:傳入一些鍵值對
常用方法:
update(Counter)
:most_common(n)
:返回 最多的n個元素的列表
8.2 deque
當只對首位數據進行操作時,雙向隊列類比list
有着更高的效率。其與list
相比,用有幾乎完全相同的API,只不過pop()
不允許按index
彈出數據,只能彈出隊尾數據,並且多了以下API:
popleft()
:與pop()
相對,從尾部彈出數據(沒有發現存在比pop(0, data)
明顯的性能優勢)appendleft()
:與append()
相對,向首部添加數據(沒有發現存在比insert(0, data)
明顯的性能優勢)
注意事項:
collections.deque
比queue.deque
更加強大;後者是對前者的封裝,作爲模塊內建對象存在,而前者這是一個類。後者擁有dequeue
、enqueue
等標準隊列操作,但個人更推薦使用前者而不是後者。
函數式編程/編程風格優化
9.functools
高階函數模塊,對於數學題或函數式編程風格優化起到重要作用。
主要方法:
reduce(function, iterable, [initializer])
:累積函數,常用於列表連續求積
其它方法跟裝飾器有關,不一一羅列。
10.itertools
本模塊實現了一系列 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)
:拆分一個迭代器爲個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
中元素組成長度爲的子序列,eg:combinations('ABCD', 2) --> AB AC AD BC BD CD
注意事項:
accumulate
可通過給定一個func
來自定義累加過程,例如給定max
作爲累加器,則統計的是至今爲止最大的數,在一些題目中相當好用。combinations
本質上就是就是計算組合數,在一些數學題中相當好用,例如print(len(list(itertools.combinations('ABCD', 2))))
可以直接得到;permutations
則是返回。
專業導向
專業導向的模塊除了
re
以外,一般不怎麼用到,只對於某些專業題目,如二進制、文件、時間、加密等。
11. datetime
時間模塊,可以直接對日期進行加減等操作,在某些日期問題中非常有用。如二月份的日期加減:
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
:時區類
12. calendar
主要用於構建日曆,在諸如打印出某年第某月日曆的題目時非常有用,如打印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)
13. struct
struct
模塊來解決bytes
和其他二進制數據類型的轉換。常用於處理二進制或字符串題。
14.re
在這些專業導向的模塊中,正則模塊則是最重要的模塊,能處理大量字符串相關的題目,需要額外學習正則表達式,此處不再贅述,屬於必須掌握的模塊。請參考>本文檔<進行學習。
15. copy
主要用於處理淺層和深層拷貝,在某些需要自定義數據結構的題目中能方便你進行對象拷貝,屬於必須掌握的模塊。主要只有兩個函數:
copy
:返回淺層拷貝。deepcopy
:耗時大,返回深層拷貝對象。
16. os.path
在一些路徑字符串分析題中非常有用,大部分情況下可以用re
模塊和str
替代。
17. string/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數字(單字節) |
18. base64
此模塊提供了將二進制數據編碼爲可打印的 ASCII 字符以及將這些編碼解碼回二進制數據的函數,在某些數據分析題中很有用,但比較冷門,偶爾會遇到,如果掌握了可以節省大量的時間。
19. difflib
此模塊提供用於比較序列的類和函數,在算法題中屬於冷門模塊,在一些用於比較字符串差異的題目中可以進行使用,例如比較 兩個字符串序列的前後差異:
c = difflib.ndiff("abcd", "abde")
for i in c:
print(i)
TLE敏感操作
上文中提到了部分敏感操作,接下來繼續總結一些:
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
關鍵字移除對象。