Python中常见算法技巧库
1、对有序数对进行插入(bisect)
import bisect
bisect.bisect_left([1,7,7,8,10],7)
返回:1
bisect.bisect_right([1,7,7,8,10],7)
返回:3
bisect.insort(list,x): 在list中插入数字,保持list的有序性。该方法会直接改变list,无返回值。(注意list需要是有序的)
2、字典自带默认类别(collections)
defaultdict(list):代表创建一个字典,并且具有默认的value的结构为list.
from collections import defaultdict
b = defaultdict(list)
b["第一个"] = '一个字符串'
b["第二个"].append(1)
b
defaultdict(list, {'第一个': '一个字符串', '第二个': [1]})
我们可以看到就算我们没有先创建一个叫做"第二个"的这个键,但是我们仍能直接使用.append(),因为字典会默认创建一个值为list的键值对。
3、转换列表为小根堆(heapq)
Python的heapq模块_junjian Li-CSDN博客
4、reduce函数的高级用法
python中reduce函数详解_bailixuance的博客-CSDN博客_python reduce
5、如何自定义位运算
我们常会遇见需要自己根据某种规则把某些东西映射到位运算去,例如我们需要把[1,2,3]->0001110(位置1,2,3分别市数字1),[4,2,1]->0010110(位置1,2,4为1)如果这两个数字做&(或运算)那么就可以得到那些位置是相同的,即含有相同字母。这种映射该怎么实现呢?
out = 0 for i in [1,2,3]: out = out | 1<<i print(bin(out)) # 展示结果
或者我需要把字符串a映射为000001,b映射为000010,c映射成000100,.....该如何操作呢?
a = 1 << (ord('a') - ord('a')) b = 1 << (ord('b') - ord('a')) c = 1 << (ord('c') - ord('a')) print(bin(a)) print(bin(b)) print(bin(c))
进一步的,如果想把abc映射成000111,aa映射成000001,ac映射成000101....这样就可以用二进制表示字符串了。(其实就是直接采用|标志符号)
a = 1 << (ord('a') - ord('a')) b = 1 << (ord('b') - ord('a')) c = 1 << (ord('c') - ord('a')) print(bin(a | b | c))
6、使用@functools.lru_cache()进行记忆化储存
我们在进行递归计算的时候常常会在一个函数中调用该函数,比如计算斐波那契数列的时候,如果你需要计算长度为5的数列时你会先算出长度为4的再算长度为...当你第二次调用的向计算长度为4的数列的时候,就不用再计算3,2...因为python内存已经储存了fun(4)(这是在计算fun(5)的时候)。使用方式:
import functools
@functools.lru_cache()
def fun(a,b):
print(a,b)
return a
fun(1,2)
比如此处定义函数fun(),传入参数a,b当执行fun(a,b)时python内部会维护一个字典,如果发现(a,b)已经运算过了就会直接返回fun(a,b)的返回值并不会执行fun(a,b)。
注意事项:
- 这里的fun函数的参数一定要是可以hash的因为这个装饰器的本质就是维护以“参数”为键,“返回值”为值的字典。
- @funtools.lru_cashe(maxsize=100)这里默认会缓存100个结果,如果超过100个结果则会对之前产生的进行覆盖,因此我们可以通过maxsize参数来控制缓存大小。
- python标准库中(functools)还提供了一个@cache装饰器,其作用也是进行缓存,但它产生的字典是全局的可以在多个函数中共享缓存,缓存的键是函数的参数,缓存的值是函数的结果,是一个更为简单的缓存器。其实本质上为@lru_cache(maxsize=None)的简写形式
7、采用collections.Counter进行快速计数
在做题目是常常会需要使用把列表中元素进行计数,比如["a","a","b","c"],我需要统计“a”有多少个,“b”有多少个...平常我们都会采用遍历数组并用字典来储存结果,我们现在可以采用collections.Counter(List)的方式,自动返回一个counter类(类似于字典)。并且我们可以在max的时候指定key这个参数,来达到直接返回字典中计数最大的字符。具体操作如下:
from collections import Counter words = ["a","a","b","c"] count = Counter(words) # count.keys()返回的是可迭代对象 # key传入一个函数,这个函数会挨个作用与可迭代对象返回的值,并将函数结果作为比较的value # 因此下面一行的含义是:count.keys()得到字典的键(["a","a","b","c"]),每个元素都经过count.get(x)得到一个value。max会返回最大的那个value所对应的x # 因此返回的是“a”,因此count.get(“a”)得到的数是2,是最大的 max(count.keys(), key=count.get)
# 返回“a”
8、使用itertools.pairwise函数连续的"重叠"对
对于一个可迭代对象来说,我想每次获取其相邻的两个元素,比如 [1,2,3,4],每次我想获得(1,2),(2,3),(3,4)这样的"重叠"对,就可以使用这个工具:
import itertools
my_list = [1,2,3,4]
for x,y in itertools.pairwise(my_list):
print(x,y)
Python| itertools之pairwise:获取连续的重叠对_python pairwise-CSDN博客
9、使用itertools.combinations()函数实现可迭代对象的组合
使用itertools可以更加高效的实现组合(combinations是每次摸出r个对象,combinations_with_replacement是有放回的摸r次每次摸一个)。
import itertools
my_list = ["a","a","b","c"]
for i in itertools.combinations(my_list, 3):
print(i)
下面看看combinations_with_replacement的效果
import itertools
my_list = ["a","a","c"]
for i in itertools.combinations_with_replacement(my_list, 2):
print(i)
更多使用itertools进行排列组合函数的例子参看:Python中的高效迭代库itertools,排列组合随便求 - 知乎 (zhihu.com)
10、使用itertools.groupby()函数实现元素聚合
有使用我们需要对一个集合/列表中的元素,按照某种方式进行聚合得到一个字典,此时我们可以使用这个函数为我们达成。但需要注意的是,使用这个函数前需要对列表中元素进行排序,因为 groupby 仅对连续相同的元素进行分组。如果序列未排序,分组的结果可能不可预测。下面是例子:
import itertools
# 待分组的
data = [('apple', 1), ('banana', 2), ('apple', 3), ('banana', 4), ('orange', 5)]
# 先根据key进行排序(此处按照水果名称)
data.sort(key=lambda x: x[0])
# 在根据key进行分组
grouped_data = itertools.groupby(data, key=lambda x: x[0])
# 遍历分组结果
for key, group in grouped_data:
print(f"Group key: {key}")
for item in group:
print(item)
在使用 groupby 时需要注意以下几点:
- 输入序列必须根据分组键(key)预先排序,否则分组结果可能不符合预期。
- groupby 返回的是一个迭代器,其中每个元素是一个元组,包含分组键和该键对应元素的迭代器。
- 由于 groupby 返回的每个分组是一个迭代器,所以它只能被遍历一次。如果需要多次使用分组数据,需要将它们转换为列表或其他数据结构。
关于第三点,我们可以使用以下代码将分类得到的结果转化为dict来储存:
# 将分组结果转换为字典
grouped_dict = {key: list(group) for key, group in grouped_data}
11、使用math包求组合数/排列数
前面使用itertool包可以对列表进行排列组合,但常常我们需要直接计算排列数和组合数,则可以使用math包的.comb和.perm计算组合数和排列数
# 使用math模块中的comb函数计算组合数C(5, 2)
math.comb(5, 2)
# 使用math模块中的perm函数计算排列数P(5, 2)
math.perm(5, 2)
12、使用itertools.accumulate求可迭代对象的前缀和
itertools.accumulate可以对序列中的元组进行累积(将前面运算得到的结果与下一个元素再次运算)操作。我们在算法中常常需要求前缀和,使用该方法可很容易的求解。
from itertools import accumulate
# 累积加法
result = list(accumulate([1, 2, 3, 4, 5]))
print(result) # 输出:[1, 3, 6, 10, 15]
# 累积乘法(自定义方法也可以)
result = list(accumulate([1, 2, 3, 4, 5], func=lambda x, y: x * y))
print(result) # 输出:[1, 2, 6, 24, 120]
# 可选参数
result = list(accumulate([1, 2, 3, 4, 5], initial=2))
print(result) # 输出:[2, 3, 5, 8, 12, 17]
这里有一个可选参数initial,表示初始第一个元素是多少,如果不给这个参数,那么初始第一个元素就为序列的第一个元素(第一个元素不会进行运算)
13、使用顺序表自动维护顺序
from sortedcontainers import SortedList # 创建一个 SortedList sorted_list = SortedList() # 向 SortedList 中添加元素 sorted_list.add(5) sorted_list.add(2) sorted_list.add(8) sorted_list.add(1) # 打印 SortedList print("SortedList:", sorted_list) # SortedList([1, 2, 5, 8]) # 检查元素是否在 SortedList 中 print("Is 5 in the list?", 5 in sorted_list) # True # 获取索引位置 print("Index of 2:", sorted_list.index(2)) # 1 # 移除元素 sorted_list.remove(8) # 打印更新后的 SortedList print("Updated SortedList:", sorted_list) # SortedList([1, 2, 5]) # 切片操作 print("Slice [1:3]:", sorted_list[1:3]) #[2, 5]